socket学习之聊天室
Posted margin_gu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了socket学习之聊天室相关的知识,希望对你有一定的参考价值。
服务端
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Windows.Forms; namespace SocketServer { public partial class Form1 : Form { /// <summary> /// 保存连接的所有用户 /// </summary> private List<User> userList = new List<User>(); /// <summary> /// 服务器IP地址 /// </summary>; private string ServerIP; /// <summary> /// 监听端口 /// </summary> private int port; private TcpListener myListener; /// <summary> /// 是否正常退出所有接收线程 /// </summary> bool isNormalExit = false; public Form1() { InitializeComponent(); btn_Stop.Enabled = false; SetServerIPAndPort(); } /// <summary> /// 根据当前程序目录的文本文件‘ServerIPAndPort.txt’内容来设定IP和端口 /// 格式:127.0.0.1:8885 /// </summary> private void SetServerIPAndPort() { FileStream fs = new FileStream("ServerIPAndPort.txt", FileMode.Open); StreamReader sr = new StreamReader(fs); string IPAndPort = sr.ReadLine(); ServerIP = IPAndPort.Split(‘:‘)[0]; //设定IP port = int.Parse(IPAndPort.Split(‘:‘)[1]); //设定端口 sr.Close(); fs.Close(); } /// <summary> /// 开始监听 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btn_Start_Click(object sender, EventArgs e) { ListenClient(); } /// <summary> /// 开始服务,监听客户端 /// </summary> private void ListenClient() { myListener = new TcpListener(IPAddress.Parse(ServerIP), port); myListener.Start(); AddTalkMessage(string.Format("开始在{0}:{1}监听客户连接", ServerIP, port)); //创建一个线程监客户端连接请求 Thread myThread = new Thread(ListenClientConnect); myThread.Start(); btn_Start.Enabled = false; btn_Stop.Enabled = true; } /// <summary> /// 接收客户端连接 /// </summary> private void ListenClientConnect() { TcpClient newClient = null; while (true) { try { newClient = myListener.AcceptTcpClient(); } catch { //当单击‘停止监听’或者退出此窗体时 AcceptTcpClient() 会产生异常 //因此可以利用此异常退出循环 break; } //每接收一个客户端连接,就创建一个对应的线程循环接收该客户端发来的信息; User user = new User(newClient); Thread threadReceive = new Thread(ReceiveData); threadReceive.Start(user); userList.Add(user); AddTalkMessage(string.Format("[{0}]进入", newClient.Client.RemoteEndPoint)); AddTalkMessage(string.Format("当前连接用户数:{0}", userList.Count)); } } /// <summary> /// 处理接收的客户端信息 /// </summary> /// <param name="userState">客户端信息</param> private void ReceiveData(object userState) { User user = (User)userState; TcpClient client = user.client; while (isNormalExit == false) { string receiveString = null; try { //从网络流中读出字符串,此方法会自动判断字符串长度前缀 receiveString = user.br.ReadString(); } catch (Exception) { if (isNormalExit == false) { AddTalkMessage(string.Format("与[{0}]失去联系,已终止接收该用户信息", client.Client.RemoteEndPoint)); RemoveUser(user); } break; } AddTalkMessage(string.Format("来自[{0}]:{1}", user.client.Client.RemoteEndPoint, receiveString)); string[] splitString = receiveString.Split(‘,‘); switch (splitString[0]) { case "Login": user.userName = splitString[1]; SendToAllClient(user, receiveString); break; case "Logout": SendToAllClient(user, receiveString); RemoveUser(user); return; case "AllTalk": string allTalkString = receiveString.Substring(splitString[0].Length + splitString[1].Length + 2); AddTalkMessage(string.Format("{0}说:{1}", user.userName, allTalkString)); for (int i = 0; i < userList.Count; i++) { SendToClient(userList[i], "talk," + user.userName + "," + allTalkString); } break; case "Talk": string talkString = receiveString.Substring(splitString[0].Length + splitString[1].Length + 2); AddTalkMessage(string.Format("{0}对{1}说:{2}", user.userName, splitString[1], talkString)); SendToClient(user, "talk," + "你对"+ splitString[1] + "说," + talkString);//发给自己 foreach (User target in userList)//发给对方 { if (target.userName == splitString[1] && user.userName != splitString[1]) { SendToClient(target, "talk," + user.userName + "对你说," + talkString); break; } } break; default: AddTalkMessage("错误:" + receiveString); break; } } } /// <summary> /// 发送消息给所有客户 /// </summary> /// <param name="user">指定发给哪个用户</param> /// <param name="message">信息内容</param> private void SendToAllClient(User user, string message) { string command = message.Split(‘,‘)[0].ToLower(); if (command == "login") { //获取所有客户端在线信息到当前登录用户 for (int i = 0; i < userList.Count; i++) { SendToClient(user, "login," + userList[i].userName); } //把自己上线,发送给所有客户端 for (int i = 0; i < userList.Count; i++) { if (user.userName != userList[i].userName) { SendToClient(userList[i], "login," + user.userName); SendToClient(userList[i], "talk,服务器:" + user.userName + "已上线,"); } } } else if (command == "logout") { for (int i = 0; i < userList.Count; i++) { if (userList[i].userName != user.userName) { SendToClient(userList[i], message); SendToClient(userList[i], "talk,服务器:" + user.userName + "已下线,"); } } } } /// <summary> /// 发送 message 给 user /// </summary> /// <param name="user">指定发给哪个用户</param> /// <param name="message">信息内容</param> private void SendToClient(User user, string message) { try { //将字符串写入网络流,此方法会自动附加字符串长度前缀 user.bw.Write(message); user.bw.Flush(); AddTalkMessage(string.Format("向[{0}]发送:{1}", user.userName, message)); } catch { AddTalkMessage(string.Format("向[{0}]发送信息失败", user.userName)); } } /// <summary> /// 移除用户 /// </summary> /// <param name="user">指定要移除的用户</param> private void RemoveUser(User user) { userList.Remove(user); user.Close(); AddTalkMessage(string.Format("当前连接用户数:{0}", userList.Count)); } private delegate void AddTalkMessageDelegate(string message); /// <summary> /// 在聊天对话框(txt_Message)中追加聊天信息 /// </summary> /// <param name="str">要追加的信息</param> private void AddTalkMessage(string message) { if (txt_Message.InvokeRequired) { AddTalkMessageDelegate d = new AddTalkMessageDelegate(AddTalkMessage); txt_Message.Invoke(d, new object[] { message }); } else { txt_Message.AppendText(message + Environment.NewLine); txt_Message.ScrollToCaret(); } } /// <summary> /// 停止监听 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btn_Stop_Click(object sender, EventArgs e) { stopListen(); } private void stopListen() { AddTalkMessage("开始停止服务,并依次使用户退出!"); isNormalExit = true; for (int i = userList.Count - 1; i >= 0; i--) { RemoveUser(userList[i]); } //通过停止监听让 myListener.AcceptTcpClient() 产生异常退出监听线程 myListener.Stop(); btn_Start.Enabled = true; btn_Stop.Enabled = false; } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { if (myListener != null) stopListen();//btn_Stop.PerformClick(); //引发 btn_Stop 的Click事件 } private void btn_send_Click(object sender, EventArgs e) { for (int i = 0; i < userList.Count; i++) { SendToClient(userList[i], "talk,服务器," + talk.Text + "\r\n"); } talk.Clear(); } private void talk_KeyPress(object sender, KeyPressEventArgs e) { if (e.KeyChar == (char)Keys.Return) { //触发【发送】按钮的单击事件 btn_send.PerformClick(); } } } }
using System; using System.Collections.Generic; using System.Text; using System.Net.Sockets; using System.IO; namespace SocketServer { class User { public TcpClient client { get; private set; } public BinaryReader br { get; private set; } public BinaryWriter bw { get; private set; } public string userName { get; set; } public User(TcpClient client) { this.client = client; NetworkStream networkStream = client.GetStream(); br = new BinaryReader(networkStream); bw = new BinaryWriter(networkStream); } public void Close() { br.Close(); bw.Close(); client.Close(); } } }
客户端
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Windows.Forms; namespace SocketClient { public partial class Form1 : Form { private string ServerIP; //IP private int port; //端口 private bool isExit = false; private TcpClient client; private BinaryReader br; private BinaryWriter bw; public Form1() { InitializeComponent(); Random r = new Random((int)DateTime.Now.Ticks); txt_UserName.Text = "user" + r.Next(100, 999); lst_OnlineUser.HorizontalScrollbar = true; lst_OnlineUser.Items.Add("所有人"); lst_OnlineUser.SelectedIndex = 0; SetServerIPAndPort(); } /// <summary> /// 根据当前程序目录的文本文件‘ServerIPAndPort.txt’内容来设定IP和端口 /// 格式:127.0.0.1:8885 /// </summary> private void SetServerIPAndPort() { try { FileStream fs = new FileStream("ServerIPAndPort.txt", FileMode.Open); StreamReader sr = new StreamReader(fs); string IPAndPort = sr.ReadLine(); ServerIP = IPAndPort.Split(‘:‘)[0]; //设定IP port = int.Parse(IPAndPort.Split(‘:‘)[1]); //设定端口 sr.Close(); fs.Close(); } catch (Exception ex) { MessageBox.Show("配置IP与端口失败,错误原因:" + ex.Message); Application.Exit(); } } /// <summary> /// 打开客户端,即连接服务器 /// </summary> private void ConnectServer() { btn_Login.Enabled = false; try { //此处为方便演示,实际使用时要将Dns.GetHostName()改为服务器域名 //IPAddress ipAd = IPAddress.Parse("182.150.193.7"); client = new TcpClient(); client.Connect(IPAddress.Parse(ServerIP), port); AddTalkMessage("连接成功\r\n"); } catch (Exception ex) { AddTalkMessage("连接失败,原因:" + ex.Message + "\r\n"); btn_Login.Enabled = true; return; } //获取网络流 NetworkStream networkStream = client.GetStream(); //将网络流作为二进制读写对象 br = new BinaryReader(networkStream); bw = new BinaryWriter(networkStream); SendMessage("Login," + txt_UserName.Text); Thread threadReceive = new Thread(new ThreadStart(ReceiveData)); threadReceive.IsBackground = true; threadReceive.Start(); } /// <summary> /// 【登陆】按钮事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btn_Login_Click(object sender, EventArgs e) { ConnectServer(); } /// <summary> /// 处理服务器信息 /// </summary> private void ReceiveData() { string receiveString = null; while (isExit == false) { try { //从网络流中读出字符串 //此方法会自动判断字符串长度前缀,并根据长度前缀读出字符串 receiveString = br.ReadString(); } catch { if (isExit == false) { MessageBox.Show("与服务器失去连接"); } break; } string[] splitString = receiveString.Split(‘,‘); string command = splitString[0].ToLower(); switch (command) { case "login": //格式: login,用户名 AddOnline(splitString[1]); break; case "logout": //格式: logout,用户名 RemoveUserName(splitString[1]); break; case "talk": //格式: talk,用户名,对话信息 AddTalkMessage(splitString[1] + ":\r\n"); AddTalkMessage(receiveString.Substring(splitString[0].Length + splitString[1].Length + 2) + "\r\n"); break; default: AddTalkMessage("错误:" + receiveString + "\r\n"); break; } } Application.Exit(); } /// <summary> /// 向服务端发送消息 /// </summary> /// <param name="message"></param> private void SendMessage(string message) { try { //将字符串写入网络流,此方法会自动附加字符串长度前缀 bw.Write(message); bw.Flush(); } catch { AddTalkMessage("发送失败\r\n"); } } private delegate void AddTalkMessageDelegate(string message); /// <summary> /// 在聊天对话框(txt_Message)中追加聊天信息 /// </summary> /// <param name="message"></param> private void AddTalkMessage(string message) { if (txt_Message.InvokeRequired) { AddTalkMessageDelegate d = new AddTalkMessageDelegate(AddTalkMessage); txt_Message.Invoke(d, new object[] { message }); } else { txt_Message.AppendText(message); txt_Message.ScrollToCaret(); } } private delegate void AddOnlineDelegate(string message); /// <summary> /// 在在线框(lst_Online)中添加其他客户端信息 /// </summary> /// <param name="userName"></param> private void AddOnline(string userName) { if (lst_OnlineUser.InvokeRequired) { AddOnlineDelegate d = new AddOnlineDelegate(AddOnline); lst_OnlineUser.Invoke(d, new object[] { userName }); } else { lst_OnlineUser.Items.Add(userName); lst_OnlineUser.SelectedIndex = lst_OnlineUser.Items.Count - 1; lst_OnlineUser.ClearSelected(); } } private delegate void RemoveUserNameDelegate(string userName); /// <summary> /// 在在线框(lst_Online)中移除不在线的客户端信息 /// </summary> /// <param name="userName"></param> private void RemoveUserName(string userName) { if (lst_OnlineUser.InvokeRequired) { RemoveUserNameDelegate d = new RemoveUserNameDelegate(RemoveUserName); lst_OnlineUser.Invoke(d, userName); } else { lst_OnlineUser.Items.Remove(userName); lst_OnlineUser.SelectedIndex = lst_OnlineUser.Items.Count - 1; lst_OnlineUser.ClearSelected(); } } /// <summary> /// 【发送】按钮单击事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btn_Send_Click(object sender, EventArgs e) { if (txt_SendText.Text == "") { MessageBox.Show("发送不能为空!"); return; } if (lst_OnlineUser.Text == "所有人" || lst_OnlineUser.Text == "") { SendMessage("AllTalk,all," + txt_SendText.Text + "\r\n"); } else { SendMessage("Talk," + lst_OnlineUser.SelectedItem + "," + txt_SendText.Text + "\r\n"); } txt_SendText.Clear(); } /// <summary> /// 在发送信息的文本框中按下【Enter】键触发的事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void txt_SendText_KeyPress(object sender, KeyPressEventArgs e) { if (e.KeyChar == (char)Keys.Return) { txt_SendText.Text = txt_SendText.Text.Substring(0, txt_SendText.Text.Length - 1); //触发【发送】按钮的单击事件 btn_Send.PerformClick(); } } /// <summary> /// 窗体关闭事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Form1_FormClosing(object sender, FormClosingEventArgs e) { //未与服务器连接前 client 为 null if (client != null) { try { SendMessage("Logout," + txt_UserName.Text); isExit = true; br.Close(); bw.Close(); client.Close(); } catch { } } } } }
实现效果:
以上是关于socket学习之聊天室的主要内容,如果未能解决你的问题,请参考以下文章