C#编写聊天软件服务端
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Net.Sockets; 6 using System.IO; 7 8 namespace SyncChatServer 9 { 10 class User 11 { 12 public TcpClient client { get; private set; } 13 public BinaryReader br { get; private set; } 14 public BinaryWriter bw { get; private set; } 15 public string userName { get; set; } 16 17 public User(TcpClient client) 18 { 19 this.client = client; 20 NetworkStream networkStream = client.GetStream(); 21 br = new BinaryReader(networkStream); 22 bw = new BinaryWriter(networkStream); 23 } 24 25 public void Close() 26 { 27 br.Close(); 28 bw.Close(); 29 client.Close(); 30 } 31 32 } 33 }
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.Windows.Forms; 9 using System.Net; 10 using System.Net.Sockets; 11 using System.Threading; 12 using System.IO; 13 14 namespace SyncChatServer 15 { 16 public partial class MainForm : Form 17 { 18 /// <summary> 19 /// 保存连接的所有用户 20 /// </summary> 21 private List<User> userList = new List<User>(); 22 23 /// <summary> 24 /// 服务器IP地址 25 /// </summary>; 26 private string ServerIP; 27 28 /// <summary> 29 /// 监听端口 30 /// </summary> 31 private int port; 32 private TcpListener myListener; 33 34 /// <summary> 35 /// 是否正常退出所有接收线程 36 /// </summary> 37 bool isNormalExit = false; 38 39 public MainForm() 40 { 41 InitializeComponent(); 42 lst_State.HorizontalScrollbar = true; 43 btn_Stop.Enabled = false; 44 SetServerIPAndPort(); 45 } 46 47 /// <summary> 48 /// 根据当前程序目录的文本文件‘ServerIPAndPort.txt’内容来设定IP和端口 49 /// 格式:127.0.0.1:8885 50 /// </summary> 51 private void SetServerIPAndPort() 52 { 53 FileStream fs = new FileStream("ServerIPAndPort.txt", FileMode.Open); 54 StreamReader sr = new StreamReader(fs); 55 string IPAndPort = sr.ReadLine(); 56 ServerIP = IPAndPort.Split(':')[0]; //设定IP 57 port = int.Parse(IPAndPort.Split(':')[1]); //设定端口 58 sr.Close(); 59 fs.Close(); 60 } 61 62 63 /// <summary> 64 /// 开始监听 65 /// </summary> 66 /// <param name="sender"></param> 67 /// <param name="e"></param> 68 private void btn_Start_Click(object sender, EventArgs e) 69 { 70 myListener = new TcpListener(IPAddress.Parse(ServerIP), port); 71 myListener.Start(); 72 AddItemToListBox(string.Format("开始在{0}:{1}监听客户连接", ServerIP, port)); 73 //创建一个线程监客户端连接请求 74 Thread myThread = new Thread(ListenClientConnect); 75 myThread.Start(); 76 btn_Start.Enabled = false; 77 btn_Stop.Enabled = true; 78 } 79 80 /// <summary> 81 /// 接收客户端连接 82 /// </summary> 83 private void ListenClientConnect() 84 { 85 TcpClient newClient = null; 86 while (true) 87 { 88 try 89 { 90 newClient = myListener.AcceptTcpClient(); 91 } 92 catch 93 { 94 //当单击‘停止监听’或者退出此窗体时 AcceptTcpClient() 会产生异常 95 //因此可以利用此异常退出循环 96 break; 97 } 98 //每接收一个客户端连接,就创建一个对应的线程循环接收该客户端发来的信息; 99 User user = new User(newClient); 100 Thread threadReceive = new Thread(ReceiveData); 101 threadReceive.Start(user); 102 userList.Add(user); 103 AddItemToListBox(string.Format("[{0}]进入", newClient.Client.RemoteEndPoint)); 104 AddItemToListBox(string.Format("当前连接用户数:{0}", userList.Count)); 105 } 106 107 } 108 109 /// <summary> 110 /// 处理接收的客户端信息 111 /// </summary> 112 /// <param name="userState">客户端信息</param> 113 private void ReceiveData(object userState) 114 { 115 User user = (User)userState; 116 TcpClient client = user.client; 117 while (isNormalExit == false) 118 { 119 string receiveString = null; 120 try 121 { 122 //从网络流中读出字符串,此方法会自动判断字符串长度前缀 123 receiveString = user.br.ReadString(); 124 } 125 catch (Exception) 126 { 127 if (isNormalExit == false) 128 { 129 AddItemToListBox(string.Format("与[{0}]失去联系,已终止接收该用户信息", client.Client.RemoteEndPoint)); 130 RemoveUser(user); 131 } 132 break; 133 } 134 AddItemToListBox(string.Format("来自[{0}]:{1}",user.client.Client.RemoteEndPoint,receiveString)); 135 string[] splitString = receiveString.Split(','); 136 switch(splitString[0]) 137 { 138 case "Login": 139 user.userName = splitString[1]; 140 SendToAllClient(user,receiveString); 141 break; 142 case "Logout": 143 SendToAllClient(user,receiveString); 144 RemoveUser(user); 145 return; 146 case "Talk": 147 string talkString = receiveString.Substring(splitString[0].Length + splitString[1].Length + 2); 148 AddItemToListBox(string.Format("{0}对{1}说:{2}",user.userName,splitString[1],talkString)); 149 SendToClient(user,"talk," + user.userName + "," + talkString); 150 foreach(User target in userList) 151 { 152 if(target.userName == splitString[1] && user.userName != splitString[1]) 153 { 154 SendToClient(target,"talk," + user.userName + "," + talkString); 155 break; 156 } 157 } 158 break; 159 default: 160 AddItemToListBox("什么意思啊:" + receiveString); 161 break; 162 } 163 } 164 } 165 166 /// <summary> 167 /// 发送消息给所有客户 168 /// </summary> 169 /// <param name="user">指定发给哪个用户</param> 170 /// <param name="message">信息内容</param> 171 private void SendToAllClient(User user, string message) 172 { 173 string command = message.Split(',')[0].ToLower(); 174 if (command == "login") 175 { 176 //获取所有客户端在线信息到当前登录用户 177 for (int i = 0; i < userList.Count; i++) 178 { 179 SendToClient(user, "login," + userList[i].userName); 180 } 181 //把自己上线,发送给所有客户端 182 for (int i = 0; i < userList.Count; i++) 183 { 184 if (user.userName != userList[i].userName) 185 { 186 SendToClient(userList[i], "login," + user.userName); 187 } 188 } 189 } 190 else if(command == "logout") 191 { 192 for (int i = 0; i < userList.Count; i++) 193 { 194 if (userList[i].userName != user.userName) 195 { 196 SendToClient(userList[i], message); 197 } 198 } 199 } 200 } 201 202 /// <summary> 203 /// 发送 message 给 user 204 /// </summary> 205 /// <param name="user">指定发给哪个用户</param> 206 /// <param name="message">信息内容</param> 207 private void SendToClient(User user, string message) 208 { 209 try 210 { 211 //将字符串写入网络流,此方法会自动附加字符串长度前缀 212 user.bw.Write(message); 213 user.bw.Flush(); 214 AddItemToListBox(string.Format("向[{0}]发送:{1}", user.userName, message)); 215 } 216 catch 217 { 218 AddItemToListBox(string.Format("向[{0}]发送信息失败",user.userName)); 219 } 220 } 221 222 /// <summary> 223 /// 移除用户 224 /// </summary> 225 /// <param name="user">指定要移除的用户</param> 226 private void RemoveUser(User user) 227 { 228 userList.Remove(user); 229 user.Close(); 230 AddItemToListBox(string.Format("当前连接用户数:{0}",userList.Count)); 231 } 232 233 private delegate void AddItemToListBoxDelegate(string str); 234 /// <summary> 235 /// 在ListBox中追加状态信息 236 /// </summary> 237 /// <param name="str">要追加的信息</param> 238 private void AddItemToListBox(string str) 239 { 240 if (lst_State.InvokeRequired) 241 { 242 AddItemToListBoxDelegate d = AddItemToListBox; 243 lst_State.Invoke(d, str); 244 } 245 else 246 { 247 lst_State.Items.Add(str); 248 lst_State.SelectedIndex = lst_State.Items.Count - 1; 249 lst_State.ClearSelected(); 250 } 251 } 252 253 /// <summary> 254 /// 停止监听 255 /// </summary> 256 /// <param name="sender"></param> 257 /// <param name="e"></param> 258 private void btn_Stop_Click(object sender, EventArgs e) 259 { 260 AddItemToListBox("开始停止服务,并依次使用户退出!"); 261 isNormalExit = true; 262 for (int i = userList.Count - 1; i >= 0; i--) 263 { 264 RemoveUser(userList[i]); 265 } 266 //通过停止监听让 myListener.AcceptTcpClient() 产生异常退出监听线程 267 myListener.Stop(); 268 btn_Start.Enabled = true; 269 btn_Stop.Enabled = false; 270 } 271 272 /// <summary> 273 /// 关闭窗口时触发的事件 274 /// </summary> 275 /// <param name="sender"></param> 276 /// <param name="e"></param> 277 private void MainForm_FormClosing(object sender, FormClosingEventArgs e) 278 { 279 if (myListener != null) 280 btn_Stop.PerformClick(); //引发 btn_Stop 的Click事件 281 } 282 } 283 }