記得八年前第一次使用socket做的一個五子棋程序,需要序列化棋子對象,傳遞到對方的電腦上。 一個偶然的機會,第二次使用socket做點事情。先看聊天服務器端的實現:
服務器端要實現以下功能:
1、啟動服務,開啟監聽
2、持續不斷地接收消息
3、發送消息
啟動服務,創建監聽socket,綁定Ip和端口:
1 /// <summary> 2 /// 啟動服務 3 /// </summary> 4 private void Start() 5 { 6 socketwatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 7 8 IPAddress address = IPAddress.Parse("10.0.0.31"); 9 10 IPEndPoint point = new IPEndPoint(address, 8025); 11 12 socketwatch.Bind(point); 13 14 //將套接字的監聽隊列長度限制為20 15 socketwatch.Listen(20); 16 17 //創建一個監聽線程 18 threadwatch = new Thread(watchconnecting); 19 20 threadwatch.IsBackground = true; 21 22 threadwatch.Start(); 23 }
創建接收數據的Socket:
1 //監聽客戶端發來的請求 2 private void watchconnecting() 3 { 4 Socket connection = null; 5 while (true) //持續不斷監聽客戶端發來的請求 6 { 7 try 8 { 9 connection = socketwatch.Accept(); 10 } 11 catch (Exception ex) 12 { 13 MessageBox.Show(ex.Message); //提示套接字監聽異常 14 break; 15 } 16 //獲取客戶端的IP和端口號 17 IPAddress clientIP = (connection.RemoteEndPoint as IPEndPoint).Address; 18 int clientPort = (connection.RemoteEndPoint as IPEndPoint).Port; 19 20 //讓客戶顯示"連接成功的"的信息 21 string sendmsg = "連接服務端成功!\r\n" + "本地IP:" + clientIP + ",本地端口" + clientPort.ToString(); 22 byte[] arrSendMsg = Encoding.UTF8.GetBytes(sendmsg); 23 connection.Send(arrSendMsg); 24 25 RemoteEndPoint = connection.RemoteEndPoint.ToString(); //客戶端網絡結點號 26 dic.Add(RemoteEndPoint, connection); //添加客戶端信息 27 28 IPEndPoint netpoint = connection.RemoteEndPoint as IPEndPoint; 29 30 //創建一個通信線程 31 ParameterizedThreadStart pts = new ParameterizedThreadStart(recv); 32 Thread thread = new Thread(pts); 33 thread.IsBackground = true;//設置為后台線程,隨着主線程退出而退出 34 //啟動線程 35 thread.Start(connection); 36 } 37 }
socketwatch.Accept() 會創建一個新的socket,它用來負責和某個客戶端的通訊。
接收數據:
1 private void recv(object socketclientpara) 2 { 3 4 Socket socketServer = socketclientpara as Socket; 5 while (true) 6 { 7 byte[] arrServerRecMsg = new byte[1024 * 1024]; 8 try 9 { 10 int length = socketServer.Receive(arrServerRecMsg); 11 12 string strSRecMsg = Encoding.UTF8.GetString(arrServerRecMsg, 0, length); 13 14 this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, 15 (ThreadStart)delegate() 16 { 17 MessagePool.Insert(MessagePool.Count, new Message() { MessageInfo = strSRecMsg, IdentityName = socketServer.RemoteEndPoint.ToString() }); 18 }); 19 } 20 catch (Exception ex) 21 { 22 MessageBox.Show(ex.Message); 23 socketServer.Close(); 24 break; 25 } 26 } 27 }
把從客戶端接收過來的字節流變為字符串,然后添加到頁面上。
發送消息:
1 private static object lockPool = new object(); 2 public static ObservableCollection<Message> MessagePool = new ObservableCollection<Message>(); 3 private void send_Click(object sender, RoutedEventArgs e) 4 { 5 string msg = this.message.Text; 6 7 this.message.Clear(); 8 9 lock (lockPool) 10 { 11 MessagePool.Insert(MessagePool.Count, new Message() { MessageInfo = msg, IdentityName = "wbq" }); 12 } 13 14 string sendMsg = msg; 15 byte[] bytes = System.Text.Encoding.UTF8.GetBytes(sendMsg); 16 17 if (dic.Count > 0) 18 dic.First().Value.Send(bytes); 19 }
dic.First().Value.Send,從字典中獲取到之前創建好的通訊socket,然后調用send方法。
客戶端需要實現以下功能:
1、和服務器建立連接
2、接收數據
3、發送數據
建立連接:
1 private void Start() 2 { 3 4 //定義一個套接字監聽 5 socketclient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 6 7 8 var servers= GetServers(); 9 10 11 if (servers.Count > 0) 12 { 13 14 //獲取文本框中的IP地址 15 IPAddress address = IPAddress.Parse(servers.First().IPToPing); 16 17 //將獲取的IP地址和端口號綁定在網絡節點上 18 IPEndPoint point = new IPEndPoint(address, int.Parse(servers.First().Port)); 19 20 try 21 { 22 //客戶端套接字連接到網絡節點上,用的是Connect 23 socketclient.Connect(point); 24 } 25 catch (Exception) 26 { 27 //MessageBox. 28 MessageBox.Show("連接失敗\r\n"); 29 return; 30 } 31 32 threadclient = new Thread(recv); 33 34 threadclient.IsBackground = true; 35 36 threadclient.Start(); 37 } 38 }
接收消息:
1 // 接收服務端發來信息的方法 2 private void recv()// 3 { 4 while (true)//持續監聽服務端發來的消息 5 { 6 try 7 { 8 //定義一個1M的內存緩沖區,用於臨時性存儲接收到的消息 9 byte[] arrRecvmsg = new byte[1024 * 1024]; 10 11 //將客戶端套接字接收到的數據存入內存緩沖區,並獲取長度 12 int length = socketclient.Receive(arrRecvmsg); 13 14 //將套接字獲取到的字符數組轉換為人可以看懂的字符串 15 string strRevMsg = Encoding.UTF8.GetString(arrRecvmsg, 0, length); 16 17 this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, 18 (ThreadStart)delegate() 19 { 20 MessagePool.Insert(MessagePool.Count, new Message() { MessageInfo = strRevMsg, IdentityName = socketclient.RemoteEndPoint.ToString() }); 21 }); 22 } 23 catch (Exception ex) 24 { 25 MessageBox.Show("遠程服務器已經中斷連接" + ex.Message); 26 break; 27 } 28 } 29 }
發送消息:
1 private void send_Click(object sender, RoutedEventArgs e) 2 { 3 string strRevMsg = this.message.Text; 4 this.message.Clear(); 5 6 lock (lockPool) 7 { 8 MessagePool.Insert(MessagePool.Count, new Message() { MessageInfo = strRevMsg, IdentityName = "我(" + socketclient.LocalEndPoint.ToString() + ")" }); 9 } 10 11 //將輸入的內容字符串轉換為機器可以識別的字節數組 12 byte[] arrClientSendMsg = Encoding.UTF8.GetBytes(strRevMsg); 13 //調用客戶端套接字發送字節數組 14 socketclient.Send(arrClientSendMsg); 15 }
經過和同事的測試,可以實現簡單的文字聊天,此代碼僅為測試代碼,若要用於實際的項目中,需要很大改進,比如socket異步建立,消息接收和發送等等。