前言: TCP是一种面向连接的,可靠的,基于字节流的传输层通讯协议。记录下Winform基于TCP协议的连天室。
介绍 服务器端 事件类型包括上线,下线,广播消息,一对一消息, 1 2 3 4 5 6 7 public enum EventType{ Exit = 0 , Online, BroadCast, OneToOne, }
封装客户端类,包含客户端套接字,客户端名称,发送消息,接收消息。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 class Client { private Socket client; public string name; private byte [] data = new byte [1024 ]; public bool IsConnected { get { return client.Connected; } } public Client (Socket client ) { this .client = client; Thread thread = new Thread(RecvMsg); thread.Start(); } public void SendMsg (string msg ) { client.Send(Encoding.UTF8.GetBytes(msg)); } public void RecvMsg () { while (true ) { try { int length = client.Receive(data); string msg = Encoding.UTF8.GetString(data, 0 , length); Console.WriteLine("收到:" + msg); string [] _data = msg.Split(':' ); this .name = _data[1 ]; Program.BroadCastMsg(msg); } catch (Exception ex) { Console.WriteLine(ex.Message); client.Shutdown(SocketShutdown.Both); client.Close(); break ; } } } }
创建tcp连接,开启线程监听客户端的连接,当有客户端连接后,将连接的客户端存入一个列表中。每次向客户端发送消息时检测是否有断开的连接,如果有就从列表中移除 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 static void Main (string [] args ){ server = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); server.Bind( new IPEndPoint(IPAddress.Parse("127.0.0.1" ), 3399 )); server.Listen(10 ); Console.WriteLine("服务器正在运行..." ); Thread th = new Thread(WatchingClient); th.Start(); } private static void WatchingClient (){ while (true ) { Socket cc = server.Accept(); Client client = new Client(cc); Console.WriteLine("新的客户端连接成功" ); clientList.Add(client); } }
当客户端上线后会给服务器端发送一个上线口令,当服务器检测到该口令后通知所有客户端更新在线列表,并把在线用户列表发给所有用户 客户端发来的口令格式为:
类型
描述
格式
Online
上线
“Online:”+用户名
Exit
下线
“Exit:”+用户名
BroadCast
群发消息
“BroadCast:”+消息内容
OneToOne
私聊消息
“OneToOne:”+”用户1^用户2^…^用户n:”+消息内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public static void BroadCastMsg (string msg ){ RemoveLogOutUser(); string [] data = msg.Split(':' ); switch (data[0 ]) { case "Online" : foreach (Client item in clientList) { if (item.IsConnected) item.SendMsg(EventType.Online + ":" + SerializeUser()); } break ; case "OneToOne" : case "BroadCast" : case "Exit" : foreach (Client client in clientList) { if (client.IsConnected) client.SendMsg(msg); } break ; } }
客户端 界面设计 待补充
登录 输入用户名后,进行登录,连接服务器,告诉服务器自己上线。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private bool Login (){ IPAddress ip = IPAddress.Parse("127.0.0.1" ); client = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { client.Connect(new IPEndPoint(ip, 3399 )); client.Send(Encoding.UTF8.GetBytes(EventType.Online + ":" + uid)); MessageBox.Show("登陆成功" ); return true ; } catch (Exception ex) { MessageBox.Show("登录失败!!" +ex.Message); return false ; } }
下线,告诉服务器自己要下线了,然后关闭连接 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private void Btn_logout_Click (object sender, EventArgs e ){ try { client.Send(Encoding.UTF8.GetBytes(EventType.Exit + ":" + uid)); client.Close(); } catch (Exception ex) { new Exception("注销失败" +ex.Message); } finally { Environment.Exit(0 ); } }
对接收到服务器端发来消息的处理及响应 由于Winform中默认无法在线程中操作UI,所以需要特殊的方法,当然方法不止一种。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 private void RecvMsgHandler (string msg ){ string [] data = msg.Split(':' ); string method = data[0 ]; Thread thread = null ; switch (method) { case "Online" : thread = new Thread(new ParameterizedThreadStart(AddOnLineUser)); thread.Start(data[1 ]); break ; case "OneToOne" : if (DeSerializeUser(data[1 ]).Contains(uid)) { thread = new Thread(new ParameterizedThreadStart(UpdateChatMsg)); thread.Start(data[2 ]); } break ; case "BroadCast" : thread = new Thread(new ParameterizedThreadStart(UpdateChatMsg)); thread.Start(data[1 ]); break ; case "Exit" : thread = new Thread(new ParameterizedThreadStart(RemoveOnLineUser)); thread.Start(data[1 ]); break ; } } private void AddOnLineUser (object obj ){ if (Lb_online_user.InvokeRequired) { Dele d = new Dele((obj1) => { Lb_online_user.Items.Clear(); List<string > cur = DeSerializeUser(obj1.ToString()); foreach (string item in cur) { Lb_online_user.Items.Add(item); } }); Lb_online_user.Invoke(d, obj); } }
发送消息 根据不同的信息类型发送不同信息口令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private void Btn_Send_Click (object sender, EventArgs e ){ if (SendMessageType.BroadCast==sendType && NullOrEmpty(Tb_edit.Text)) MessageBox.Show("发送的消息不能为空" ); if ((SendMessageType.OneToOne==sendType && GetUserCheckedCount()==0 ) || NullOrEmpty(Tb_edit.Text)) MessageBox.Show("发送的消息不能为空,发送成员不能为空" ); else { switch (sendType) { case SendMessageType.BroadCast: client.Send( Encoding.UTF8.GetBytes( EventType.BroadCast + ":" + uid + "群发:" + Tb_edit.Text)); break ; case SendMessageType.OneToOne: client.Send( Encoding.UTF8.GetBytes( EventType.OneToOne + ":" + SerializeUser()+ ":" + uid + "私发:" + Tb_edit.Text)); break ; } Tb_edit.Text = "" ; } }
完整代码 链接:https://pan.baidu.com/s/1nDbiIxLaBPveQKLg00UMYw 密码:h6kd