上篇博文:
介绍了客户端连接服务端,一对一,多对一的情况,下面实现服务器接收消息的功能。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方法接收参数及返回值。
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 }
接收消息的代码:
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 else22 {23 //socket没办法发送空消息 如果收到空消息 客户端关闭24 ShowMsg(ip + ":" + "断开连接");25 connSocket.Close();26 break;27 28 }29 30 }31 32 }
测试:仍然用telnet命令来测试:telnet 127.0.0.1 50000
测试结果:多对一,一对一,发送消息正常,关闭客户端,服务端正常显示哪个客户端断开连接。
服务器端所有代码:
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 Dictionarydic = new Dictionary (); 26 private void btnSend_Click(object sender, EventArgs e) 27 { 28 ServerSendMsg(this.txtInputMsg.Text); 29 } 30 /// 31 /// 服务器给客户端发送消息 32 /// 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 else109 {110 //socket没办法发送空消息 如果收到空消息 客户端关闭111 ShowMsg(ip + ":" + "断开连接");112 connSocket.Close();113 break;114 }115 116 }117 118 }119 ///120 /// 提示信息辅助方法121 /// 122 /// 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 }
客户端实现
客户端创建的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.ChatClient14 {15 public partial class Client : Form16 {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 ///60 /// 客户端向服务端发送消息61 /// 62 /// 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 else74 {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 //客户端关闭 关闭socket92 socket.Shutdown(SocketShutdown.Both);93 }94 }95 } 请先连接服务器>
测试结果:
结语:边学习,边动手,实现了两端通信,目前支持一对一,多对一通信功能,代码中针对关闭客户端的情况还有bug,有待进一步修改。