感觉之前的聊天功能可能有一些缺陷,今天收到书华兄的启发,发表一些感慨,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
====================== 相互学习,共同进步 ===================