使用Socket模拟聊天室
前言
Socket 是对 TCP/IP 协议的封装,Socket 只是个接口不是协议,通过 Socket 我们才能使用 TCP/IP 协议,除了 TCP,也可以使用 UDP 协议来传递数据。
本文不对TCP/IP进行深入理解阐释,仅仅针对WinForm中如何使用Socket的几个关键点进行阐述。
Server
采用的是C/S架构,在某一时刻,服务器只能与一个客户端建立连接。
1、初始化,将本地IP作为Server的IP地址,端口号可以随意指定。
public ServerForm() { InitializeComponent(); IPAddress[] localIP = Dns.GetHostAddresses(Dns.GetHostName()); //Get any IP foreach (IPAddress address in localIP) { if (address.AddressFamily == AddressFamily.InterNetwork) { tb_IPAdress.Text = address.ToString(); } } }
2、创建监听实例,启动Server
private void StartServer_button_Click(object sender, EventArgs e) { new System.Threading.Thread(s => { try { TcpListener listener = new TcpListener(IPAddress.Any, int.Parse(textBox5.Text)); listener.Start(); client = listener.AcceptTcpClient(); strRead = new StreamReader(client.GetStream()); strWrite = new StreamWriter(client.GetStream()); strWrite.AutoFlush = true; Listen_backgroundWorker.RunWorkerAsync(); //Start recieving data Listen_backgroundWorker.WorkerSupportsCancellation = true; } catch (Exception x) { MessageBox.Show(x.Message.ToString()); } }) { IsBackground = true }.Start(); }
如代码所示,新建了一个线程——线程启动了一个指定IP地址和端口号的服务端,并开始监听读写请求。如下的Listen_backgroundWorker线程,则对接收到的消息在UI上进行显示:
private void Listen_backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { while (client.Connected) { try { recieve = strRead.ReadLine(); this.Receive_textBox.Invoke(new MethodInvoker(delegate () { Receive_textBox.AppendText("C发送 : " + recieve + "\n"); })); } catch (Exception x) { MessageBox.Show(x.Message.ToString()); } } }
3、Server向已与之建立连接的Client发送消息:
private void Send_button_Click(object sender, EventArgs e) { if (SendtextBox.Text != "") { textToSend = SendtextBox.Text; Send_backgroundWorker.RunWorkerAsync(); } SendtextBox.Text = ""; }
private void Send_backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { if (client.Connected) { strWrite.WriteLine(textToSend); this.Receive_textBox.Invoke(new MethodInvoker(delegate () { Receive_textBox.AppendText("S发送 : " + textToSend + "\n"); })); } else { MessageBox.Show("Send Failed!"); } Send_backgroundWorker.CancelAsync(); }
Client
1、将Client连接至指定IP地址和端口号Server。
private void ConnectToServer_button_Click(object sender, EventArgs e) { try { client = new TcpClient(); IPEndPoint ip_End = new IPEndPoint(IPAddress.Parse(textBox6.Text), int.Parse(textBox4.Text)); client.Connect(ip_End); if (client.Connected) { Receive_textBox.AppendText("Connected to server" + "\n"); stw = new StreamWriter(client.GetStream()); str = new StreamReader(client.GetStream()); stw.AutoFlush = true; Receive_backgroundWorker.RunWorkerAsync(); } } catch (Exception x) { MessageBox.Show(x.Message.ToString()); } }
如下代码,在Client连接的到Server之后,也开始可以随时接收由Server传来的消息:
private void Receive_backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { while (client.Connected) { try { recieve = str.ReadLine(); this.Receive_textBox.Invoke(new MethodInvoker(delegate () { Receive_textBox.AppendText("S发送 : " + recieve + "\n"); })); } catch (Exception x) { MessageBox.Show(x.Message.ToString()); } } }
2、与Server建立连接的Client可以随时发送消息:
private void Send_button_Click(object sender, EventArgs e) { if (Send_textBox.Text != "") { textToSend = Send_textBox.Text; Send_backgroundWorker.RunWorkerAsync(); } Send_textBox.Text = ""; } private void Send_backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { if (client.Connected) { stw.WriteLine(textToSend); this.Receive_textBox.Invoke(new MethodInvoker(delegate () { Receive_textBox.AppendText("C发送 : " + textToSend + "\n"); })); } else { MessageBox.Show("Send Failed!"); } Send_backgroundWorker.CancelAsync(); }
运行结果:
源码:https://github.com/Deskew/SocketChat