上篇博文:http://www.cnblogs.com/wolf-sun/p/3329558.htmlhtml
介紹了客戶端鏈接服務端,一對一,多對一的狀況,下面實現服務器接收消息的功能。LZ這些弄的比較慢,也是邊學習,邊動手實現的。具體步驟在註釋中寫的比較清楚,不懂的能夠留言,LZ會盡快回復。共同窗習,共同進步。數組
接收消息時機緩存
何時接收消息?當服務器開始監聽,有客戶端鏈接,而且鏈接成功,此時負責通訊的Socket已經建立,此時就能夠接收消息了,能夠經過Socket的Receive()方法接收消息。服務器
1 // 摘要: 2 // 從綁定的 System.Net.Sockets.Socket 套接字接收數據,將數據存入接收緩衝區。 3 // 4 // 參數: 5 // buffer: 6 // System.Byte 類型的數組,它是存儲接收到的數據的位置。 7 // 8 // 返回結果: 9 // 接收到的字節數。 10 // 11 // 異常: 12 // System.ArgumentNullException: 13 // buffer 爲 null。 14 // 15 // System.Net.Sockets.SocketException: 16 // 試圖訪問套接字時發生錯誤。 有關更多信息,請參見備註部分。 17 // 18 // System.ObjectDisposedException: 19 // System.Net.Sockets.Socket 已關閉。 20 // 21 // System.Security.SecurityException: 22 // 調用堆棧中的調用方沒有所需的權限。 23 public int Receive(byte[] buffer);
上面代碼介紹了Receive方法接收參數及返回值。socket
1 private void ListenConn(object o) 2 { 3 //將參數o 轉化爲監聽的socket 4 Socket socketListener = o as Socket; 5 //寫入循環 每個鏈接就建立一個通訊用的socket 6 while (true) 7 { 8 //當有客戶端鏈接成功 建立通訊用的socket 9 Socket connSocket = socketListener.Accept(); 10 string ip = connSocket.RemoteEndPoint.ToString(); 11 ShowMsg(ip + " " + DateTime.Now.ToString() + " 鏈接成功"); 12 //建立一個新線程去接收消息 13 Thread th = new Thread(ReceiveMsg); 14 th.Start(connSocket); 15 16 } 17
18 }
接收消息的代碼:ide
1 //接收客戶端的消息 2 private void ReceiveMsg(object o) 3 { 4 Socket connSocket = o as Socket; 5 6 //通訊用的socket鏈接成功 就能夠接收消息了 7 byte[] buffer = new byte[1024 * 1024 * 5];//5M緩存 8 while (true) 9 { 10 //count是當前接收的字節個數 11 int count = connSocket.Receive(buffer); 12 string ip = connSocket.RemoteEndPoint.ToString(); 13 //判斷接收到的字節個數 是0表示客戶端關閉了 14 if (count > 0) 15 { 16 17 //將字節轉換爲字符串 18 string msg = Encoding.UTF8.GetString(buffer, 0, count); 19 ShowMsg(ip + " " + DateTime.Now.ToString() + "\r\n" + msg); 20 } 21 else 22 { 23 //socket沒辦法發送空消息 若是收到空消息 客戶端關閉 24 ShowMsg(ip + ":" + "斷開鏈接"); 25 connSocket.Close(); 26 break; 27 28 } 29 30 } 31 32 }
測試:仍然用telnet命令來測試:telnet 127.0.0.1 50000
學習
測試結果:多對一,一對一,發送消息正常,關閉客戶端,服務端正常顯示哪一個客戶端斷開鏈接。測試
服務器端全部代碼:this
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Net; 8 using System.Net.Sockets; 9 using System.Text; 10 using System.Threading; 11 using System.Threading.Tasks; 12 using System.Windows.Forms; 13 14 namespace Wolfy.ChatServer 15 { 16 public partial class Server : Form 17 { 18 public Server() 19 { 20 InitializeComponent(); 21 //不讓其檢查跨線程的操做 22 Control.CheckForIllegalCrossThreadCalls = false; 23 } 24 //存放endpoin和通訊用的socket 25 Dictionary<string, Socket> dic = new Dictionary<string, Socket>(); 26 private void btnSend_Click(object sender, EventArgs e) 27 { 28 ServerSendMsg(this.txtInputMsg.Text); 29 } 30 /// <summary> 31 /// 服務器給客戶端發送消息 32 /// </summary> 33 private void ServerSendMsg(string msg) 34 { 35 //服務器給客戶端發消息 36 string userkey = comboBoxEndpoint.Text; 37 if (!string.IsNullOrEmpty(userkey)) 38 { 39 ShowMsg(msg); 40 byte[] buffer = Encoding.UTF8.GetBytes(msg); 41 dic[userkey].Send(buffer); 42 msg = ""; 43 } 44 else 45 { 46 MessageBox.Show("請選擇客戶端"); 47 } 48 } 49 50 private void btnStartService_Click(object sender, EventArgs e) 51 { 52 //服務器ip地址 53 IPAddress ip = IPAddress.Parse(txtIPAddress.Text); 54 //ip地址和端口 55 IPEndPoint endpoint = new IPEndPoint(ip, int.Parse(txtPort.Text)); 56 //建立用於監聽的socket 57 Socket socketListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 58 //綁定ip和端口 59 socketListener.Bind(endpoint); 60 //開始監聽 限制鏈接數 最多能夠鏈接10個 61 socketListener.Listen(10); 62 ShowMsg("開始監聽......"); 63 //建立線程 去監聽鏈接 64 Thread th = new Thread(ListenConn); 65 //將線程變爲後臺線程 66 th.IsBackground = true; 67 th.Start(socketListener); 68 } 69 private void ListenConn(object o) 70 { 71 //將參數o 轉化爲監聽的socket 72 Socket socketListener = o as Socket; 73 //寫入循環 每個鏈接就建立一個通訊用的socket 74 while (true) 75 { 76 //當有客戶端鏈接成功 建立通訊用的socket 77 Socket connSocket = socketListener.Accept(); 78 string ip = connSocket.RemoteEndPoint.ToString(); 79 ShowMsg(ip + " " + DateTime.Now.ToString() + " 鏈接成功"); 80 //鏈接成功後加入字典 81 dic.Add(ip, connSocket); 82 comboBoxEndpoint.Items.Add(ip); 83 //建立一個新線程去接收消息 84 Thread th = new Thread(ReceiveMsg); 85 th.Start(connSocket); 86 } 87 88 } 89 //接收客戶端的消息 90 private void ReceiveMsg(object o) 91 { 92 Socket connSocket = o as Socket; 93 94 //通訊用的socket鏈接成功 就能夠接收消息了 95 byte[] buffer = new byte[1024 * 1024 * 5];//5M緩存 96 while (true) 97 { 98 //count是當前接收的字節個數 99 int count = connSocket.Receive(buffer); 100 string ip = connSocket.RemoteEndPoint.ToString(); 101 //判斷接收到的字節個數 是0表示客戶端關閉了 102 if (count > 0) 103 { 104 //將字節轉換爲字符串 105 string msg = Encoding.UTF8.GetString(buffer, 0, count); 106 ShowMsg(ip + " " + DateTime.Now.ToString() + "\r\n" + msg); 107 } 108 else 109 { 110 //socket沒辦法發送空消息 若是收到空消息 客戶端關閉 111 ShowMsg(ip + ":" + "斷開鏈接"); 112 connSocket.Close(); 113 break; 114 } 115 116 } 117 118 } 119 /// <summary> 120 /// 提示信息輔助方法 121 /// </summary> 122 /// <param name="msg"></param> 123 private void ShowMsg(string msg) 124 { 125 this.txtMsgView.AppendText(msg + "\r\n"); 126 } 127 128 private void txtInputMsg_KeyUp(object sender, KeyEventArgs e) 129 { 130 //若是用戶按下了Enter鍵 131 if (e.KeyCode == Keys.Enter) 132 { 133 //則調用 服務器向客戶端發送信息的方法 134 ServerSendMsg(this.txtInputMsg.Text); 135 } 136 } 137 } 138 }
客戶端實現spa
客戶端建立的socket即負責鏈接又負責通訊,因此這裏和服務端不一樣。客戶端鏈接、接收消息和發送消息代碼以下:
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Windows.Forms; 10 using System.Net.Sockets; 11 using System.Net; 12 using System.Threading; 13 namespace Wolf.ChatClient 14 { 15 public partial class Client : Form 16 { 17 public Client() 18 { 19 InitializeComponent(); 20 //不讓檢查跨線程操做 21 Control.CheckForIllegalCrossThreadCalls = false; 22 } 23 Socket socket; 24 private void btnStartService_Click(object sender, EventArgs e) 25 { 26 //鏈接服務器的ip和端口 27 IPAddress ip = IPAddress.Parse(txtIPAddress.Text); 28 IPEndPoint endpoint = new IPEndPoint(ip, int.Parse(txtPort.Text)); 29 //客戶端的socket即負責鏈接又負責通訊 30 socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 31 //鏈接服務器 32 socket.Connect(endpoint); 33 ShowMsg("鏈接成功......"); 34 //和服務器鏈接成功後就能夠接收服務端的消息了 35 Thread th = new Thread(ReceiveMsg); 36 th.IsBackground = true; 37 th.Start(); 38 39 } 40 private void ReceiveMsg() 41 { 42 byte[] buffer = new byte[1024 * 1024 * 5]; 43 while (true) 44 { 45 int count = socket.Receive(buffer); 46 string msg = Encoding.UTF8.GetString(buffer, 0, count); 47 ShowMsg(this.txtIPAddress.Text + ":" + this.txtPort.Text + " " + DateTime.Now.ToString() + "\r\n" + msg); 48 } 49 } 50 private void ShowMsg(string msg) 51 { 52 txtMsgView.AppendText(msg + "\r\n"); 53 } 54 private void btnSend_Click(object sender, EventArgs e) 55 { 56 ClientSendMsg(this.txtInputMsg.Text); 57 } 58 59 /// <summary> 60 /// 客戶端向服務端發送消息 61 /// </summary> 62 /// <param name="msg"></param> 63 private void ClientSendMsg(string msg) 64 { 65 //向服務端發送消息 66 if (socket != null) 67 { 68 ShowMsg(msg); 69 byte[] buffer = Encoding.UTF8.GetBytes(msg); 70 socket.Send(buffer); 71 msg = ""; 72 } 73 else 74 { 75 ShowMsg("<<<<請先鏈接服務器>>>"); 76 } 77 } 78 79 private void txtInputMsg_KeyUp(object sender, KeyEventArgs e) 80 { 81 //若是用戶按下了Enter鍵 82 if (e.KeyCode == Keys.Enter) 83 { 84 //則調用 服務器向客戶端發送信息的方法 85 ClientSendMsg(this.txtInputMsg.Text); 86 } 87 } 88 89 private void Client_FormClosing(object sender, FormClosingEventArgs e) 90 { 91 //客戶端關閉 關閉socket 92 socket.Shutdown(SocketShutdown.Both); 93 } 94 } 95 }
測試結果:
結語:
邊學習,邊動手,實現了兩端通訊,目前支持一對一,多對一通訊功能,代碼中針對關閉客戶端的狀況還有bug,有待進一步修改。