感觉之前的聊天功能可能有一些缺陷,今天收到书华兄的启发,发表一些感慨,C# .net高手跟新手的区别就是,新手仅仅满足功能上的实现,而很少去考虑性能上的问题,比如高并发怎么处理,打个比方,新手做完聊天功能之后,会沾沾自喜,而不考虑最大客户连接有多少,做个实验,如果连续有四个左右的客户端同时连接到服务器端,服务器就要消耗将近200M的内存,这时机器可怕的事情,如果不做处理的话,假设几百人去连接,估计服务器端就要崩溃了,还有一点高手喜欢写封装,将功能进行高度封装给新手用,新手用完了人家封装的类库,然后简单代码就实现功能了,也会沾沾自喜,津津乐道,这样就会止步不前!用了一下封装的类库来写unity聊天客户端果真非常快,几行代码就搞定!
unity客户端:
UI:
using UnityEngine; using System.Collections; public class chart : MonoBehaviour { string txtmsg = ""; public string txtServer = "192.168.1.24"; string txtLog = ""; public string txtPort = "50000"; ClientLibrary.Client client; ClientLibrary.Model model = new ClientLibrary.Model(); // Use this for initialization void Start () { } bool flag = true; // Update is called once per frame void Update () { if(client.HeartDataSend()!=1) { SetTxt("请检查网络你已经掉线,可能原因服务器出错,请重新连接"); client.KillSocket(); flag = false; } } void OnGUI() { txtServer = GUI.TextField(new Rect(10f,20f,100f,20f),txtServer); txtLog = GUI.TextField (new Rect(50f,40f,200f,200f),txtLog); if(GUI.Button(new Rect(120f,20f,50f,20f),"start")&&flag) { model.TxtServer = txtServer; model.TxtPort = txtPort; bool enable; client = new ClientLibrary.Client(model,SetTxt,out enable); if(enable) { //txtLog = "123"; } else { //txtLog = "no"; } flag = false; } } void SetTxt(string msg) { txtLog+=msg+"\r\n"; } }
ChatClient:
using System.Net.Sockets; using System.Text; using System.Threading; namespace ClientLibrary { #region 委托 /// <summary> /// 信息显示委托 /// </summary> /// <param name="msg">需要打印到前台的信息</param> public delegate void deMessageShow(string msg); /// <summary> /// 文件保存委托 /// </summary> /// <param name="buffer">保存的文件数据</param> /// <param name="count">文件大小</param> /// <returns></returns> public delegate bool deGetFileStream(byte[] buffer,int count); /// <summary> /// 客户端震动 委托 /// </summary> public delegate void deZD(); #endregion /// <summary> /// 客户端实体类 /// </summary> public class Model { private string txtMsg; public string TxtMsg { get { return txtMsg; } set { txtMsg = value; } } private string txtPort; public string TxtPort { get { return txtPort; } set { txtPort = value; } } private string txtServer; public string TxtServer { get { return txtServer; } set { txtServer = value; } } } /// <summary> /// 客户端方法类 /// </summary> public class Client { public Client(Model model,deMessageShow messageShowFun,out bool enable) { messageShow = messageShowFun; lastTimeRecive = DateTime.Now; if (CreateClientSocket(model)) //创建Socket { enable = true; return; } enable = false; } public Client(Model model, deMessageShow message, deGetFileStream saveFileFun, out bool enable) { messageShow = message; getFileStream = saveFileFun; lastTimeRecive = DateTime.Now; if (CreateClientSocket(model))//创建Socket { enable = true; return; } enable = false; } public Client(Model model, deMessageShow message, deGetFileStream saveFileFun, deZD zdFun, out bool enable) { messageShow = message; getFileStream = saveFileFun; lastTimeRecive = DateTime.Now; if (!CreateClientSocket(model)) //创建Socket { enable = true; return; } enable = false; } Model model = new Model(); /// <summary> /// 通信Scoket /// </summary> Socket connSocket; /// <summary> /// 通信线程 /// </summary> Thread thmsg; /// <summary> /// 最后一次接受数据时间 /// </summary> public DateTime lastTimeRecive; /// <summary> /// 最后一次发送数据时间 /// </summary> public DateTime lastTimeSend; #region 声明委托 public deMessageShow messageShow; public deGetFileStream getFileStream; public deZD zD; #endregion /// <summary> /// 创建客户端Socket /// </summary> /// <param name="ModelUI"></param> /// <returns>创建状态</returns> public bool CreateClientSocket(Model ModelUI) { model = ModelUI; bool enabel = false; //服务器IP IPAddress ipServer = IPAddress.Parse(model.TxtServer); //创建 终端地址 IPEndPoint endPointServer = new IPEndPoint(ipServer, int.Parse(model.TxtPort)); //创建 连接socket connSocket connSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { connSocket.Connect(endPointServer); messageShow("登陆成功"); #region 创建接受信息的线程 thmsg = new Thread(ReciveMsg); thmsg.IsBackground = true; thmsg.Start(); #endregion } catch (Exception ex) { messageShow(ex.Message); enabel= true; } return enabel; } /// <summary> /// 信息接收 /// </summary> private void ReciveMsg() { //0 文字 1 文件 2 震动 byte[] buffer = new byte[5 * 1024 * 1024]; while (true) { int count = connSocket.Receive(buffer); int flag = buffer[0]; if (count == 0) { connSocket.Close(); messageShow("你掉线或者服务器关系"); break; } Recive(buffer, count, flag); } } #region 必要方法 #region 接收+Recive(byte[] buffer, int count,int flag) /// <summary> /// 处理接受的信息 /// </summary> /// <param name="buffer"></param> /// <param name="count"></param> /// <param name="flag"></param> private void Recive(byte[] buffer, int count, int flag) { if (flag == 0) { #region flag=0 文字信息 string msg = Encoding.UTF8.GetString(buffer, 1, count - 1); messageShow("服务器 : " + msg); #endregion } else if (flag == 1) { #region flag=1 传送文件 if (getFileStream(buffer,count)) { messageShow("接受完成"); } #endregion } else if (flag == 2) { #region flag=2 震动 zD(); #endregion } else if (flag == 3) { string atat=Encoding.UTF8.GetString(buffer,1,count-1); if (atat == "ATAT") { lastTimeRecive = DateTime.Now; } } } #endregion /// <summary> /// 发送心跳包 /// </summary> /// <returns>发送状态</returns> public int HeartDataSend() { lastTimeSend = DateTime.Now; if (DateTime.Now.Second - lastTimeRecive.Second > 3) { return 3; } if (connSocket != null && connSocket.Connected) { try { byte[] buffer = Encoding.UTF8.GetBytes("NTNT"); connSocket.Send(buffer); lastTimeRecive = DateTime.Now; return 1; } catch (Exception ex) { // messageShow(ex.Message); return 2; } } return 1; } /// <summary> /// 数据发送 /// </summary> /// <returns></returns> public bool Send() { if (connSocket != null && connSocket.Connected) { try { byte[] buffer = new byte[1024 * 1024 * 5]; buffer = Encoding.UTF8.GetBytes(model.TxtMsg); connSocket.Send(buffer); lastTimeRecive = DateTime.Now; return true; } catch (Exception ex) { messageShow(ex.Message); return false; } } return false; } /// <summary> /// 终止连接 /// </summary> public void KillSocket() { if (thmsg != null) { thmsg.Abort(); } if (connSocket != null && connSocket.Connected) { connSocket.Shutdown(SocketShutdown.Both); connSocket.Close(); } } #endregion } }
服务器端:
Server:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; namespace ServerChatClass { #region 委托 /// <summary> /// 信息显示委托 /// </summary> /// <param name="msg">需要打印到前台的信息</param> public delegate void deMessageShow(string msg); /// <summary> /// 注册客户端ednpoing和Socket,Thread /// </summary> /// <param name="endpoing">注册地址</param> public delegate void deRegisterClientIP(string endpoing); /// <summary> /// 从UI上移除客户端显示 /// </summary> /// <param name="endpoing">客户端地址或者名称</param> public delegate void deRemoveClientIPFromUI(string endpoing); #endregion /// <summary> /// 实体类 /// </summary> public class Model { private string txtMsg; private string txtPath; private string txtLog; private string txtServer; private string txtPort; private string clientEndPoint; /// <summary> /// 客户端地址 /// </summary> public string ClientEndPoint { get { return clientEndPoint; } set { clientEndPoint = value; } } /// <summary> /// 端口 /// </summary> public string TxtPort { get { return txtPort; } set { txtPort = value; } } /// <summary> /// IP /// </summary> public string TxtServer { get { return txtServer; } set { txtServer = value; } } /// <summary> /// 日志 /// </summary> public string TxtLog { get { return txtLog; } set { txtLog = value; } } /// <summary> /// 文件地址 /// </summary> public string TxtPath { get { return txtPath; } set { txtPath = value; } } /// <summary> /// 消息文字发送 /// </summary> public string TxtMsg { get { return txtMsg; } set { txtMsg = value; } } } public class Request { public Request(string msg) { if (msg.Length < 100) { return; } try { string[] lines = msg.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); string firstLine = lines[0]; string[] array = firstLine.Split(' '); method = array[0]; url = array[1]; } catch (Exception) { } } private string method; public string Method { get { return method; } set { method = value; } } private string url; public string Url { get { return url; } set { url = value; } } } public class SeverChar { #region 客户机注册字典 /// <summary> /// 客户端地址clientIP和对应最后一次通信时间 /// </summary> public Dictionary<string, DateTime> dicListTimeConnSocket = new Dictionary<string, DateTime>(); /// <summary> /// 客户端地址clinetIP和对应的通信Socket /// </summary> public Dictionary<string, Socket> dicConnSocket = new Dictionary<string, Socket>(); /// <summary> /// 客户端地址clientIP和对应通信线程ThreadMsg /// </summary> public Dictionary<string, Thread> dicThreadMsg = new Dictionary<string, Thread>(); #endregion #region 委托声明 /// <summary> /// 消息显示 /// </summary> public deMessageShow messageShow; /// <summary> /// 注册客户机 /// </summary> public deRegisterClientIP registerClientIP; /// <summary> /// 从UI上移除客户端显示 /// </summary> public deRemoveClientIPFromUI removeClientIPFromUI; #endregion /// <summary> /// 初始化程序 /// </summary> /// <param name="downline">掉线信息</param> public SeverChar(string msg,Model model, deMessageShow messageShowFun, string downline, out bool enable) { messageShow = messageShowFun; if (CreateServerMonitorSorcket(model,msg)) //创建Socket { enable = true; } else { enable = false; } } public SeverChar(string msg,Model model, deMessageShow messageShowFun,deRegisterClientIP registerClientIPFun ,string downline, out bool enable) { registerClientIP = registerClientIPFun; messageShow = messageShowFun; if (CreateServerMonitorSorcket(model, msg))//创建Socket { enable = true; } else { enable = false; } } public SeverChar(string msg,Model model, deMessageShow messageShowFun, deRegisterClientIP registerClientIPFun, deRemoveClientIPFromUI removerClientIPFormUIFun,string downline, out bool enable) { removeClientIPFromUI = removerClientIPFormUIFun; registerClientIP = registerClientIPFun; messageShow = messageShowFun; if (CreateServerMonitorSorcket(model, msg)) //创建Socket { enable = true; } else { enable=false; } } public SeverChar(string downline) { downLine = downline; } /// <summary> /// 需要发送请求的通信Socket /// </summary> public List<Socket> listSendConnSocket = new List<Socket>(); /// <summary> /// 掉线信息提示 /// </summary> public string downLine; Socket socketServer;//监听socket Model modelSever = new Model(); object o = new object(); #region 创建监听的socket socketServer+public bool CreateServerMonitorSorcket(Model model, string msg) /// <summary> /// 服务器创建监听的socket socketServer /// </summary> /// <param name="model">监听实体,包含监听端口和IP</param> /// <param name="msg">监听成功后显示的信息</param> /// <returns></returns> public bool CreateServerMonitorSorcket(Model model, string msg) { modelSever = model; bool enable; Socket socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); # region 其他测试 //IPHostEntry host = new IPHostEntry(); //host. #endregion //Ip地址 IPAddress ipServer = IPAddress.Parse(model.TxtServer); //ip地址和端口 IPEndPoint endPointServer = new IPEndPoint(ipServer, int.Parse(model.TxtPort)); try { //绑定网络端点 socketServer.Bind(endPointServer); //设置排队长度 socketServer.Listen(10); messageShow(msg); #region 用连接连接线程ThreadConnSockt 创建通信Socket 防止阻塞卡死+Listen(object) Thread threadConnSocket = new Thread(Listen); threadConnSocket.Name = "conn"; threadConnSocket.IsBackground = true; threadConnSocket.Start(socketServer); #endregion enable = true; } catch (Exception ex) { messageShow(ex.Message); enable = false; } return enable; } #endregion #region 创建服务端通信Socket connSocket单线程会阻塞窗体运行+Listen(object) /// <summary> /// 创建通信Socket /// </summary> /// <param name="socket"></param> public void Listen(object socket) { socketServer = socket as Socket; while (true) { Socket connSocket = socketServer.Accept();//阻塞等待 客户端连接 string clinetEndPing = connSocket.RemoteEndPoint.ToString();//获取客户端通信地址 messageShow(clinetEndPing+"登陆"); #region 新建通信监听线程threadMsg 接收客户端发送过来的信息 Thread threadMsg = new Thread(ReciveMsg); threadMsg.Name = "msg"; threadMsg.IsBackground = true; threadMsg.Start(connSocket); RegisterClientAtSever(clinetEndPing, threadMsg, connSocket); #endregion } } #endregion #region 接收客户端发送的信息 ReciveMsg(object socket) /// <summary> /// 接收客户端发送的信息 方法 /// </summary> /// <param name="socket">发送信息的socket</param> public void ReciveMsg(object socket) { Socket connSocket = socket as Socket; string clineteEndPoint = connSocket.RemoteEndPoint.ToString(); byte[] buffer = new byte[1024 * 1024 * 5]; while (true) { int count = connSocket.Receive(buffer);//阻塞等待输入 当count=0 说明连接关闭 if (count == 0) { dicConnSocket[clineteEndPoint].Close(); messageShow(clineteEndPoint + downLine); RemoveClient(clineteEndPoint); } dicListTimeConnSocket[clineteEndPoint] = DateTime.Now;//有消息传入 #region 协议解析 string ntnt = Encoding.UTF8.GetString(buffer, 0, 4); if (ntnt != "NTNT")//不是心跳报就显示 { string msg = Encoding.UTF8.GetString(buffer, 0, count); messageShow(clineteEndPoint + " : \r\n " + msg); } #endregion else { lock (o) { List<Socket> listSendConnSocket = new List<Socket>(); listSendConnSocket.Add(dicConnSocket[clineteEndPoint]); SendStreamFunction(listSendConnSocket, 3, modelSever); listSendConnSocket.Clear(); } } } } #endregion #region 发送方法+public void SendStreamFunction(List<Socket> listSendConnSocket, int flag, Model model) /// <summary> /// 发送方法 /// </summary> /// <param name="listSendConnSocket"></param> /// <param name="flag"></param> /// <param name="model"></param> public void SendStreamFunction(List<Socket> listSendConnSocket, int flag, Model model, params byte[] buffersend) { if (listSendConnSocket.Count > 0) { List<byte> list = new List<byte>(); if (flag == 0) { byte[] buffer = new byte[5 * 1024 * 1024]; buffer = Encoding.UTF8.GetBytes(model.TxtMsg); list.Add(0);//协议 0 文字 list.AddRange(buffer); } else if (flag == 1) { if (model.TxtPath != null) { using (FileStream fs = new FileStream(model.TxtPath, FileMode.Open, FileAccess.Read)) { byte[] buffer = new byte[fs.Length]; fs.Read(buffer, 0, buffer.Length); list.Add(1);//协议 1 文件 list.AddRange(buffer); } } } else if (flag == 2) { list.Add(2); } else if (flag == 3)//没有收到任何通信的协议 一定时间 协议3 { byte[] buffer = Encoding.UTF8.GetBytes("ATAT");//NTNT心跳报 list.Add(3); list.AddRange(buffer); } else if (flag == 4) { if (buffersend != null) { list.AddRange(buffersend); } } foreach (Socket item in listSendConnSocket) { item.Send(list.ToArray()); } } } #endregion #region 定时检测方法+public void Timer() /// <summary> /// 定时检测通信状态 /// </summary> public void Timer() { int index = 0; Dictionary<int, string> dic = new Dictionary<int, string>(); foreach (KeyValuePair<string, DateTime> item in dicListTimeConnSocket) { dic.Add(index, item.Key); index++; } DateTime timeNow = DateTime.Now; for (int i = 0; i < dicListTimeConnSocket.Count; i++) { if (timeNow.Second - dicListTimeConnSocket[dic[i]].Second > 3) { RemoveClient(dic[i]); } } } #endregion #region 客户端在服务器注册+public void RegisterClientAtSever(string endPoint, Thread threadMsg, Socket connSocket) /// <summary> /// 客户端注册 /// </summary> /// <param name="endPoint">客户端地址</param> /// <param name="threadMsg">通信线程</param> /// <param name="connSocket">通信Socket</param> public void RegisterClientAtSever(string endPoint, Thread threadMsg, Socket connSocket) { dicThreadMsg.Add(endPoint, threadMsg); dicConnSocket.Add(endPoint, connSocket); modelSever.ClientEndPoint = endPoint; if (registerClientIP != null) { registerClientIP(endPoint);//注册显示到 用户UI上 通过委托 registerClientIP } } #endregion #region 从服务器注销客户端连接+RemoveClient(string clinetEndPoint) /// <summary> /// 从服务器注销客户端连接 /// </summary> /// <param name="clinetEndPoint"></param> public void RemoveClient(string clinetEndPoint) { KillClient(clinetEndPoint); dicThreadMsg.Remove(clinetEndPoint); dicConnSocket.Remove(clinetEndPoint); dicListTimeConnSocket.Remove(clinetEndPoint); if (removeClientIPFromUI != null) { removeClientIPFromUI(clinetEndPoint); } } #endregion #region 终止客户端通信+public void KillClient(string clinetEndPoint) /// <summary> /// 终止客户端通信 /// </summary> /// <param name="clinetEndPoint"></param> public void KillClient(string clinetEndPoint) { if (dicConnSocket[clinetEndPoint].Connected) { dicConnSocket[clinetEndPoint].Shutdown(SocketShutdown.Both); } dicConnSocket[clinetEndPoint].Close(); dicThreadMsg[clinetEndPoint].Abort(); } #endregion } }
ServerWinform:
using System; using System.Collections.Generic; using System.Threading; using System.Windows.Forms; using ServerChatClass; namespace ChatServer { public partial class ServerChar : Form { public ServerChar() { InitializeComponent(); Control.CheckForIllegalCrossThreadCalls = false; } //Socket connSocket;//通信socket // Thread thmsg;//接收消息 线程 ServerChatClass.SeverChar severchar; Model model = new Model(); private void btnStart_Click(object sender, EventArgs e) { bool enable; model.TxtServer = txtServer.Text; model.TxtPort = txtPort.Text; severchar = new SeverChar("开始监听",model, ShowLog, add, RemoveControl, "掉线",out enable); if (enable) { btnStart.Enabled = false; timer1.Start(); } else { btnStart.Enabled = true; } } void add(string clinetIP) { listIp.Items.Add(clinetIP); cboUsers.Items.Add(clinetIP); } public void ShowLog(string msg) { txtLog.AppendText(msg + "\r\n"); } #region 发送 private void Send(object sender, EventArgs e) { int flag = 0; Button btn = sender as Button; if (btn.Text == "发送消息") { model.TxtMsg = txtMsg.Text; flag = 0; } else if (btn.Text == "发送文件") { model.TxtPath = txtPath.Text; flag = 1; } else if (btn.Text == "震动") { flag = 2; } severchar.SendStreamFunction(severchar.listSendConnSocket, flag, model); } //private void SendMsg(object sender, EventArgs e) //{ //} //private void btnSendFile_Click(object sender, EventArgs e) //{ // model.TxtPath = txtPath.Text; // severchar.SendStreamFunction(severchar.listSendConnSocket, 1, model); //} //private void btnZD_Click(object sender, EventArgs e) //{ // severchar.SendStreamFunction(severchar.listSendConnSocket, 2, model); //} #endregion private void btnSelect_Click(object sender, EventArgs e) { OpenFileDialog open = new OpenFileDialog(); if (open.ShowDialog() == System.Windows.Forms.DialogResult.OK) { txtPath.Text = open.FileName; } } private void DownLine_Click(object sender, EventArgs e) { for (int i = 0; i < listIp.SelectedItems.Count; i++) { string clinetIP = listIp.SelectedItems[i].ToString(); severchar.RemoveClient(clinetIP); } } private void RemoveControl(string clinetIP) { cboUsers.Items.Remove(clinetIP); listIp.Items.Remove(clinetIP); } private void listIp_SelectedIndexChanged(object sender, EventArgs e) { severchar.listSendConnSocket.Clear(); if (listIp.SelectedItems.Count == 1) { cboUsers.SelectedIndex = listIp.SelectedIndex; } foreach (string item in listIp.SelectedItems) { severchar.listSendConnSocket.Add(severchar.dicConnSocket[item]); } } private void Server_FormClosing(object sender, FormClosingEventArgs e) { if (severchar.dicThreadMsg.Count > 0) { int index = 0; Dictionary<int, string> dic = new Dictionary<int, string>(); foreach (KeyValuePair<string, Thread> item in severchar.dicThreadMsg) { dic.Add(index, item.Key); index++; } DateTime timeNow = DateTime.Now; for (int i = 0; i < severchar.dicThreadMsg.Count; i++) { severchar.RemoveClient(dic[i]); } } } private void timer1_Tick(object sender, EventArgs e) { if( severchar.dicListTimeConnSocket.Count>0) { severchar.Timer(); } } } }
下载:http://download.csdn.net/detail/s10141303/6675157
==================== 迂者 丁小未 CSDN博客专栏=================
MyBlog:http://blog.csdn.net/dingxiaowei2013 MyQQ:1213250243
MyTel:13262983383
====================== 相互学习,共同进步 ===================
本文转蓬莱仙羽51CTO博客,原文链接:http://blog.51cto.com/dingxiaowei/1366190,如需转载请自行联系原作者