多人聊天室範例

 範例三-多人聊天室
連線程式的伺服器有很多時候都必須用到多人連線,以下就為各位示範基礎的多人連線伺服器。

 圖1-4為伺服器執行成功的畫面,可以看到下方的訊息欄顯示" Server is start.",也就是服務器啟動,伺服器只有中轉客戶發言及廣播的功能。
server3.JPG
圖1-4

 圖1-5為使用模擬器開啟客戶端後連入伺服器的展示圖,可以看到下方有兩個方塊第一個方塊示輸入客戶名稱,第二方塊是在輸入要說的話,按鈕是確定送出,用意在於讓伺服端可以中轉客戶端發話的訊息,讓伺服器廣播給其它客戶端。
Client.JPG
圖1-5

 以下為客戶端與伺服端的程式碼

 多人聊天程式-客戶端
專案下載:ClientSocketDemo3.rar

package ccc.ClientSocketDemo3;
 
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
 
import ccc.ClientSocketDemo3.R;
 
// 
public class ClientSocketDemo3 extends Activity {
    public static Handler mHandler = new Handler();
    TextView TextView01;    // 用來顯示文字訊息
    EditText EditText01;    // 文字方塊
    EditText EditText02;    // 文字方塊
    String tmp;                // 暫存文字訊息
    Socket clientSocket;    // 客戶端socket
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        // 從資源檔裡取得位址後強制轉型成文字方塊
        TextView01 = (TextView) findViewById(R.id.TextView01);
        EditText01=(EditText) findViewById(R.id.EditText01);
        EditText02=(EditText) findViewById(R.id.EditText02);
 
        // 以新的執行緒來讀取資料
        Thread t = new Thread(readData);
 
        // 啟動執行緒
        t.start();
 
        // 從資源檔裡取得位址後強制轉型成按鈕
        Button button1=(Button) findViewById(R.id.Button01);
 
        // 設定按鈕的事件
        button1.setOnClickListener(new Button.OnClickListener() {        
            // 當按下按鈕的時候觸發以下的方法
            public void onClick(View v) {
                // 如果已連接則
                if(clientSocket.isConnected()){
 
                    BufferedWriter bw;
 
                    try {
                        // 取得網路輸出串流
                        bw = new BufferedWriter( new OutputStreamWriter(clientSocket.getOutputStream()));
 
                        // 寫入訊息
                        bw.write(EditText01.getText()+":"+EditText02.getText()+"\n");
 
                        // 立即發送
                        bw.flush();
                    } catch (IOException e) {
 
                    }
                    // 將文字方塊清空
                    EditText02.setText("");
                }    
            }
        });
 
    }
 
    // 顯示更新訊息
    private Runnable updateText = new Runnable() {
        public void run() {
            // 加入新訊息並換行
            TextView01.append(tmp + "\n");
        }
    };
 
    // 取得網路資料
    private Runnable readData = new Runnable() {
        public void run() {
            // server端的IP
            InetAddress serverIp;
 
            try {
                // 以內定(本機電腦端)IP為Server端
                serverIp = InetAddress.getByName("10.0.2.2");
                int serverPort = 5050;
                clientSocket = new Socket(serverIp, serverPort);
 
                // 取得網路輸入串流
                BufferedReader br = new BufferedReader(new InputStreamReader(
                        clientSocket.getInputStream()));
 
                // 當連線後
                while (clientSocket.isConnected()) {
                    // 取得網路訊息
                    tmp = br.readLine();
 
                    // 如果不是空訊息則
                    if(tmp!=null)
                        // 顯示新的訊息
                        mHandler.post(updateText);
                }
 
            } catch (IOException e) {
 
            }
        }
    };
 
}

 多人聊天程式-伺服端
SocketServerDemo3.rar
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
 
public class SocketServerDemo3 {
 
    private static int serverport = 5050;
    private static ServerSocket serverSocket;
 
    // 用串列來儲存每個client
    private static ArrayList<Socket> players=new ArrayList<Socket>();
 
    // 程式進入點
    public static void main(String[] args) {
        try {
            serverSocket = new ServerSocket(serverport);
            System.out.println("Server is start.");
 
            // 當Server運作中時
            while (!serverSocket.isClosed()) {
                // 顯示等待客戶端連接
                System.out.println("Wait new clinet connect");
 
                // 呼叫等待接受客戶端連接
                waitNewPlayer();
            }
 
        } catch (IOException e) {
            System.out.println("Server Socket ERROR");
        }
 
    }
 
    // 等待接受客戶端連接
    public static void waitNewPlayer() {
        try {
            Socket socket = serverSocket.accept();
 
            // 呼叫創造新的使用者
            createNewPlayer(socket);
        } catch (IOException e) {
 
        }
 
    }
 
    // 創造新的使用者
    public static void createNewPlayer(final Socket socket) {
 
        // 以新的執行緒來執行
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 增加新的使用者
                    players.add(socket);
 
                    // 取得網路串流 
                    BufferedReader br = new BufferedReader(
                            new InputStreamReader(socket.getInputStream()));
 
                    // 當Socket已連接時連續執行
                    while (socket.isConnected()) {
                        // 取得網路串流的訊息
                        String msg= br.readLine();
 
                        // 輸出訊息 
                        System.out.println(msg);
 
                        // 廣播訊息給其它的客戶端
                        castMsg(msg);
                    }
 
                } catch (IOException e) {
 
                }
 
                // 移除客戶端
                players.remove(socket);            
            }
        });
 
        // 啟動執行緒
        t.start();
    }
 
    // 廣播訊息給其它的客戶端
    public static void castMsg(String Msg){
        // 創造socket陣列
        Socket[] ps=new Socket[players.size()]; 
 
        // 將players轉換成陣列存入ps
        players.toArray(ps);
 
        // 走訪ps中的每一個元素
        for (Socket socket :ps ) {
            try {
                // 創造網路輸出串流
                BufferedWriter bw;
                bw = new BufferedWriter( new OutputStreamWriter(socket.getOutputStream()));
 
                // 寫入訊息到串流
                bw.write(Msg+"\n");
 
                // 立即發送
                bw.flush();
            } catch (IOException e) {
 
            }
        }
    }
}

 Activity介面程式碼
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
 
<LinearLayout android:id="@+id/FrameLayout01" android:layout_height="wrap_content" android:layout_width="fill_parent">
 
<EditText android:id="@+id/EditText01" android:layout_height="wrap_content" android:text="player01" android:layout_width="wrap_content" android:layout_weight="1"></EditText>
<EditText android:id="@+id/EditText02" android:layout_height="wrap_content" android:text="msg" android:layout_width="wrap_content" android:layout_weight="10"></EditText>
<Button android:id="@+id/Button01" android:layout_height="wrap_content" android:text="say" android:layout_gravity="right" android:layout_width="wrap_content" android:layout_weight="1"></Button>
 
</LinearLayout>
<TextView 
    android:id="@+id/TextView01" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text=""
    />
</LinearLayout>

 AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="ccc.ClientSocketDemo3"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".ClientSocketDemo1"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 
    </application>
    <uses-sdk android:minSdkVersion="4" />
 
    <!-- 這是註解 -->
    <uses-permission android:name="android.permission.INTERNET" />  
    <!--  由於需使用到網路的功能,所以必須加上面這行來取得權限-->
</manifest>

 程式影片解說