接上文代码:
#region 异步接收数据的回调函数
public static void ReadCallback(IAsyncResult ar)
{
//缓冲区大小
StateObject state = null;
var sb = new StringBuilder();
byte[] reciveData;
ModuleSocketInfo msi;
//线程使用
SocketActivateCertification socketActivateCertification;
SocketHeartbeat socketHeartbeat;
SocketUpdateIP socketUpdateIP;
SocketTakingPictures socketTakingPictures;
SocketFile socketFile;
Thread t;
try
{ state = (StateObject)ar.AsyncState;
Handler = state.workSocket;
var moduleIp = Handler.RemoteEndPoint.ToString();//放映机IP
//客户端发送数据的长度
var bytesRead = Handler.EndReceive(ar);
//输出日志信息
var printData = new byte[bytesRead];
Array.Copy(state.buffer, 0, printData, 0, bytesRead);
var printDataInfo = CommonTools.BytesToString(printData);
LogInfo.Instance.SetMessage("INFO", "原始数据,接收放映机IP:" + moduleIp + ",发送的报文长度为:" + bytesRead + ",数据为:" + printDataInfo); if (FrameData == null)//不需要处理粘包问题
{
//数据长度=缓存区长度
reciveData = new byte[bytesRead];
//数据拷贝
Array.Copy(state.buffer, 0, reciveData, 0, bytesRead);
}
else//处理粘包问题
{
//数据长度=缓存区长度+粘包数据长度
reciveData = new byte[bytesRead + FrameData.Length];
//组包数据拷贝到reciveData
Array.Copy(FrameData, 0, reciveData, 0, FrameData.Length);
//接收数据拷贝到reciveData
Array.Copy(state.buffer, 0, reciveData, FrameData.Length, bytesRead);
//重新设置为null
FrameData = null;
} //接收的数据长度>0
if (reciveData.Length > 0)
{ for (var i = 0; i < reciveData.Length; i++)
{
//校验包头,第三个字节为正文长度,如果不做判断,在下面取长度的时候会报错
if (reciveData[i] == 0x5A && reciveData.Length > 3)
{
//报文长度
var messageLength = reciveData[i + 2] + 9; //9表示帧头和帧尾长度
//接收的报文长度>=实际报文长度
if (reciveData.Length >= messageLength)
{
//定义报文长度
var tempMessage = new byte[messageLength];
frameMessageLength += tempMessage.Length; //存储帧长度
//赋值报文(将原来的i改成了现在的0,主要是因为碰到5A。5A时有问题)
Array.Copy(reciveData, i, tempMessage, 0, tempMessage.Length);
//完整的一帧报文
var fullMesssage = CommonTools.BytesToString(tempMessage);
LogInfo.Instance.SetMessage("INFO",
"逻辑处理,接收放映机IP:" + moduleIp + ",一帧完整的报文:" + fullMesssage); //-------待优化---------
//线程只是创建一次,数据存储在队列中
lock (SocketServer.HtFyjInfo)
{
LogInfo.Instance.SetMessage("INFO", "SocketServer");
PrintHtFyjInfo();
if (SocketServer.HtFyjInfo.Count > 0)
{
if (SocketServer.HtFyjInfo.Contains(moduleIp))
{
msi = (ModuleSocketInfo)SocketServer.HtFyjInfo[moduleIp];
if (msi != null)
{
//校验包头
if (tempMessage[0] == 0x5A)
{
//报文类型
switch (tempMessage[1])
{
case 0x01: //模块请求激活报文
//实例化
msi.message0x01 = tempMessage;
//赋值
//Array.Copy(reciveData, 0, msi.message0x01, 0,
// reciveData.Length);
//调用线程
socketActivateCertification =
new SocketActivateCertification(msi.message0x01,
moduleIp);
t =
new Thread(
new ThreadStart(
socketActivateCertification
.ThreadParsingMessage0x01));
t.IsBackground = true;
t.Start();
break;
//其他报文也是同样处理,接收到数据后,启动一个线程来处理
}
}
else
{
LogInfo.Instance.SetMessage("INFO",
"包头校验错误,数据抛弃,错误包头为:" + reciveData[0]);
}
}
else
{
LogInfo.Instance.SetMessage("INFO", "集合中没有放映机:" + moduleIp + "的记录");
}
}
}
}
//-------待优化---------
//判断是否存在下一帧
var nextFrame = reciveData.Length - frameMessageLength;
if (nextFrame != 0) //存在下一帧
{
i = messageLength - 1; //for循环的时候i会++ }
else
{
i = frameMessageLength; //从一个完整的帧开始循环
frameMessageLength = 0; //在一个完整的帧的时候,重置为0
}
}
else //接收的报文长度<实际报文长度
{
//存储在组包数据中
FrameData = new byte[reciveData.Length];
Array.Copy(reciveData, 0, FrameData, 0, FrameData.Length);
break;
}
}
else
{
LogInfo.Instance.SetMessage("INFO", "包头校验错误数据抛弃,包头为:" + reciveData[i]);
}
}
//if (Handler != null && Handler.Connected)
//{ //} ////继续异步接收
Handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
else
{
//使用测试工具的时候,会进去此逻辑
LogInfo.Instance.SetMessage("INFO", "放映机:" + moduleIp + ",断开连接");
SocketServer.HtFyjInfo.Remove(Handler.RemoteEndPoint.ToString());
LogInfo.Instance.SetMessage("INFO", "集合中删除:" + Handler.RemoteEndPoint.ToString());
PrintHtFyjInfo();
}
}
catch (SocketException ex)
{
}
catch (ArgumentException ex)
{
}
catch (Exception ex)
{
}
}
#endregion
#region 异步接收数据的回调函数
public static void ReadCallback(IAsyncResult ar)
{
//缓冲区大小
StateObject state = null;
var sb = new StringBuilder();
byte[] reciveData;
ModuleSocketInfo msi;
//线程使用
SocketActivateCertification socketActivateCertification;
SocketHeartbeat socketHeartbeat;
SocketUpdateIP socketUpdateIP;
SocketTakingPictures socketTakingPictures;
SocketFile socketFile;
Thread t;
try
{ state = (StateObject)ar.AsyncState;
Handler = state.workSocket;
var moduleIp = Handler.RemoteEndPoint.ToString();//放映机IP
//客户端发送数据的长度
var bytesRead = Handler.EndReceive(ar);
//输出日志信息
var printData = new byte[bytesRead];
Array.Copy(state.buffer, 0, printData, 0, bytesRead);
var printDataInfo = CommonTools.BytesToString(printData);
LogInfo.Instance.SetMessage("INFO", "原始数据,接收放映机IP:" + moduleIp + ",发送的报文长度为:" + bytesRead + ",数据为:" + printDataInfo); if (FrameData == null)//不需要处理粘包问题
{
//数据长度=缓存区长度
reciveData = new byte[bytesRead];
//数据拷贝
Array.Copy(state.buffer, 0, reciveData, 0, bytesRead);
}
else//处理粘包问题
{
//数据长度=缓存区长度+粘包数据长度
reciveData = new byte[bytesRead + FrameData.Length];
//组包数据拷贝到reciveData
Array.Copy(FrameData, 0, reciveData, 0, FrameData.Length);
//接收数据拷贝到reciveData
Array.Copy(state.buffer, 0, reciveData, FrameData.Length, bytesRead);
//重新设置为null
FrameData = null;
} //接收的数据长度>0
if (reciveData.Length > 0)
{ for (var i = 0; i < reciveData.Length; i++)
{
//校验包头,第三个字节为正文长度,如果不做判断,在下面取长度的时候会报错
if (reciveData[i] == 0x5A && reciveData.Length > 3)
{
//报文长度
var messageLength = reciveData[i + 2] + 9; //9表示帧头和帧尾长度
//接收的报文长度>=实际报文长度
if (reciveData.Length >= messageLength)
{
//定义报文长度
var tempMessage = new byte[messageLength];
frameMessageLength += tempMessage.Length; //存储帧长度
//赋值报文(将原来的i改成了现在的0,主要是因为碰到5A。5A时有问题)
Array.Copy(reciveData, i, tempMessage, 0, tempMessage.Length);
//完整的一帧报文
var fullMesssage = CommonTools.BytesToString(tempMessage);
LogInfo.Instance.SetMessage("INFO",
"逻辑处理,接收放映机IP:" + moduleIp + ",一帧完整的报文:" + fullMesssage); //-------待优化---------
//线程只是创建一次,数据存储在队列中
lock (SocketServer.HtFyjInfo)
{
LogInfo.Instance.SetMessage("INFO", "SocketServer");
PrintHtFyjInfo();
if (SocketServer.HtFyjInfo.Count > 0)
{
if (SocketServer.HtFyjInfo.Contains(moduleIp))
{
msi = (ModuleSocketInfo)SocketServer.HtFyjInfo[moduleIp];
if (msi != null)
{
//校验包头
if (tempMessage[0] == 0x5A)
{
//报文类型
switch (tempMessage[1])
{
case 0x01: //模块请求激活报文
//实例化
msi.message0x01 = tempMessage;
//赋值
//Array.Copy(reciveData, 0, msi.message0x01, 0,
// reciveData.Length);
//调用线程
socketActivateCertification =
new SocketActivateCertification(msi.message0x01,
moduleIp);
t =
new Thread(
new ThreadStart(
socketActivateCertification
.ThreadParsingMessage0x01));
t.IsBackground = true;
t.Start();
break;
//其他报文也是同样处理,接收到数据后,启动一个线程来处理
}
}
else
{
LogInfo.Instance.SetMessage("INFO",
"包头校验错误,数据抛弃,错误包头为:" + reciveData[0]);
}
}
else
{
LogInfo.Instance.SetMessage("INFO", "集合中没有放映机:" + moduleIp + "的记录");
}
}
}
}
//-------待优化---------
//判断是否存在下一帧
var nextFrame = reciveData.Length - frameMessageLength;
if (nextFrame != 0) //存在下一帧
{
i = messageLength - 1; //for循环的时候i会++ }
else
{
i = frameMessageLength; //从一个完整的帧开始循环
frameMessageLength = 0; //在一个完整的帧的时候,重置为0
}
}
else //接收的报文长度<实际报文长度
{
//存储在组包数据中
FrameData = new byte[reciveData.Length];
Array.Copy(reciveData, 0, FrameData, 0, FrameData.Length);
break;
}
}
else
{
LogInfo.Instance.SetMessage("INFO", "包头校验错误数据抛弃,包头为:" + reciveData[i]);
}
}
//if (Handler != null && Handler.Connected)
//{ //} ////继续异步接收
Handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
else
{
//使用测试工具的时候,会进去此逻辑
LogInfo.Instance.SetMessage("INFO", "放映机:" + moduleIp + ",断开连接");
SocketServer.HtFyjInfo.Remove(Handler.RemoteEndPoint.ToString());
LogInfo.Instance.SetMessage("INFO", "集合中删除:" + Handler.RemoteEndPoint.ToString());
PrintHtFyjInfo();
}
}
catch (SocketException ex)
{
}
catch (ArgumentException ex)
{
}
catch (Exception ex)
{
}
}
#endregion
/// 发送给客户端
/// </summary>
/// <param name="handler">socket</param>
/// <param name="byteData">字节信息</param>
public static void Send(Socket handler, byte[] byteData)
{
// Convert the string data to byte data using ASCII encoding.
//byte[] byteData = Encoding.ASCII.GetBytes(data);
string sendMessgae = CommonTools.BytesToString(byteData);
LogInfo.Instance.SetMessage("INFO", "采集回发放映报文:" + sendMessgae);
// Begin sending the data to the remote device.
handler.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), handler);
} public static void SendCallback(IAsyncResult ar)
{
try
{
Socket handler = (Socket)ar.AsyncState;
int bytesSent = handler.EndSend(ar);
LogInfo.Instance.SetMessage("INFO", "发送成功,总字节数:" + bytesSent);
}
catch (Exception ex)
{
LogError.Instance.SetMessage("ERROR", ex.ToString());
}
}
#endregion
没事别老阻塞代码
//存储客户端IP和Message0x75报文
public static Hashtable HtMessaeg0X75 = new Hashtable();
//存储放映机信息
public static Hashtable HtFyjInfo = new Hashtable();
public static Socket Handler = null;
public static byte[] FrameData;//帧数据,用于粘包处理
private static int frameMessageLength = 0;
它就很成问题。
Hashtable过于原始和含糊,你应该具有更加明确强类型的定义。
客户端有多少个,Buffer也就有多个。怎么可能只有一个Socket和一个Buffer呢?
其实既没法并发也不是异步的
你能骗自己,但是骗不了编译器只有一个静态socket,那么当然一个客户端在发送的时候突然有另一个客户端连进来,它就指向另一个客户端,而把前一个客户端的句柄给丢弃了
下面一段server端代码,供参考 private IPEndPoint serverInfo;//存放服务器的IP和端口信息
private Socket serverSocket;//服务端运行的SOCKET
private Thread serverThread;//服务端运行的线程
private Socket[] clientSocket;//为客户端建立的SOCKET连接
private int clientNumb;//存放客户端数量
private byte[] msgBuffer;//存放消息数据 private void btnStart_Click(object sender, EventArgs e)
{
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverInfo = new IPEndPoint(IPAddress.Any, this.GetPort());
serverSocket.Bind(serverInfo);
serverSocket.Listen(10);
clientSocket = new Socket[65535];
msgBuffer = new byte[65535];
clientNumb = 0;
serverThread = new Thread(new ThreadStart(ReceiveAccept));
serverThread.Start();
CheckForIllegalCrossThreadCalls = false;//不捕获对错误线程的调用
this.btnStart.Enabled = false;
this.btnEnd.Enabled = true;
this.labelMsg.Text = "服务正在运行..." + " 运行端口:" + this.GetPort().ToString();
this.lbClients.Items.Add("服务于 " + DateTime.Now.ToString() + " 开始运行.");
} private void ReceiveAccept()
{
while (true)
{
//Accept 以同步方式从侦听套接字的连接请求队列中提取第一个挂起的连接请求,然后创建并返回新的 Socket。
//在阻止模式中,Accept 将一直处于阻止状态,直到传入的连接尝试排入队列。连接被接受后,原来的 Socket 继续将传入的连接请求排入队列,直到您关闭它。
clientSocket[clientNumb] = serverSocket.Accept();
clientSocket[clientNumb].BeginReceive(msgBuffer, 0, msgBuffer.Length, SocketFlags.None,
new AsyncCallback(ReceiveCallback), clientSocket[clientNumb]);
lock (this.lbClients)
{
this.lbClients.Items.Add(clientSocket[clientNumb].RemoteEndPoint.ToString() + " 成功连接服务器.");
}
clientNumb++;
}
} private void ReceiveCallback(IAsyncResult ia)
{
try
{
Socket s = (Socket)ia.AsyncState;
int iEnd= s.EndReceive(ia);
for (int i = 0; i < clientNumb; i++)
{
if (clientSocket[i].Connected)
clientSocket[i].Send(msgBuffer, 0, iEnd, SocketFlags.None);
s.BeginReceive(msgBuffer, 0, msgBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), s);
}
}
catch
{ }
}
我觉得你有必要了解一下线程池的概念,socket完全可以是动态new出来,而不是事先定义好的
即使是动态new出来的socket想要保存句柄,也完全可以使用List<socket>之类的结构来存放,而不是什么都用Hashtable存
我觉得你有必要了解一下线程池的概念,socket完全可以是动态new出来,而不是事先定义好的
即使是动态new出来的socket想要保存句柄,也完全可以使用List<socket>之类的结构来存放,而不是什么都用Hashtable存
谢谢指点,我稍后了解下线程池
之所以有用Hashtable存储是因为key存储的是客户端的连接IP,客户端本身是不带有编号信息的,
为了方便查找才用Hashtable存储的
//客户端IP
var clientIp = IPAddress.Parse(((IPEndPoint)handler.RemoteEndPoint).Address.ToString()) + ":" + ((IPEndPoint)handler.RemoteEndPoint).Port.ToString(CultureInfo.InvariantCulture);
//信息显示
var clientRunInfo = "放映机:" + clientIp + "与数据采集已连接";
LogInfo.Instance.SetMessage("INFO", clientRunInfo);
MainForm.AppendRunInfo(clientRunInfo); //将和采集建立连接的放映机存储至哈希表中
var mss = new ModuleSocketInfo()
{
handleSocket = handler,
connectionState = "0"//未连接,仅仅是建立socket连接,而非和放映机建立连接
};
mss.CumulativeSerialNumber();
HtFyjInfo.Add(clientIp, mss);
至于性能方面,我想无非是客户端的数量越多,对于资源的消耗,服务器是否能吃的消。跟线程关系不大
楼主的服务端代码很有问题..
异步操作应该为每一个TCP联接创建一个独立的接收数据对像用来拆包发送等.
发送数据需要用对应的Socet对像发送,
关于异步发送可以参考如下:http://blog.csdn.net/daonidedie/article/details/41345673
/// <summary>
/// 用于辅助异步发送Socket的帮肋类
/// </summary>
public class AsyncSocketSendHelper
{ #region 客户端联接池
/// <summary>
/// 接收SAEA池集合
/// </summary>
private ConcurrentStack<SocketAsyncEventArgs> ReceiveSocketArgsPool = new ConcurrentStack<SocketAsyncEventArgs>(); /// <summary>
/// 初始化接收socketargs对象池
/// </summary>
protected void InitReceiveSocketArgsPool()
{
for (int ndx = 0; ndx < 3000 ; ndx++)
{
ReceiveSocketArgsPool.Push(CreateReceiveSocketArgs());
}
} /// <summary>
/// 创建一个接收SAEA对象,设置最大接收字节数
/// </summary>
/// <returns></returns>
protected virtual SocketAsyncEventArgs CreateReceiveSocketArgs()
{
SocketAsyncEventArgs e = new SocketAsyncEventArgs();
e.Completed += IO_SendComleted;
return e;
}
/// <summary>
/// 租赁一个接收SAEA对象
/// </summary>
/// <returns></returns>
protected virtual SocketAsyncEventArgs RentReveiveSocketArgs()
{
SocketAsyncEventArgs e; return ReceiveSocketArgsPool.TryPop(out e) ? e : CreateReceiveSocketArgs();
} /// <summary>
/// 归还一个接收SAEA参数
/// </summary>
/// <param name="e">还池</param>
protected virtual void GivebackReceiveSocketArgs(SocketAsyncEventArgs e)
{
if (e != null)
{
e.UserToken = null;
ReceiveSocketArgsPool.Push(e);
}
} #endregion /// <summary>
/// 发送数据服务
/// </summary>
/// <param name="socket">用于发送的Socket对像</param>
/// <param name="buff">需要发送的数据</param>
/// <param name="callBack">回调函数参数为:发送结果,目标节点,发送数据</param>
/// <param name="userToken">用户数据 </param>
/// <returns></returns>
public virtual void SendToAsync(Socket socket, byte[] buff, Action<AsyncSendResult> callBack = null, object userToken = null)
{
if (socket == null)
{
throw new NullReferenceException();
} if (buff == null)
{
throw new NullReferenceException();
} SocketAsyncEventArgs e = RentReveiveSocketArgs();
//设置发送缓冲区
e.SetBuffer(buff, 0, buff.Length);
try
{
//用户标识
var token = new AsyncSendResult
{
Result = false,
RemoteEndPoint = socket.RemoteEndPoint.ToString(),
SendTime = DateTime.Now,
SendData = buff,
ResultTime = DateTime.Now,
CallBakc = callBack,
UserToKen = userToken,
};
e.UserToken = token;
//发送数据
if (!socket.SendAsync(e))
{
IO_SendComleted(socket, e);
}
}
catch (SocketException)
{
//还池
GivebackReceiveSocketArgs(e);
}
catch (ObjectDisposedException)
{
//还池
GivebackReceiveSocketArgs(e);
}
} /// <summary>
/// 发送数据后的完成功能
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void IO_SendComleted(object sender, SocketAsyncEventArgs e)
{
try
{
if (e.UserToken != null)
{
AsyncSendResult token = e.UserToken as AsyncSendResult;
if (token != null)
{
//设置结果时间
token.ResultTime = DateTime.Now; //发送数据OK
if (e.SocketError == SocketError.Success)
{
token.Result = true;
if (token.CallBakc != null)
{
token.CallBakc(token);
} }
else
{
//发送数据失败
token.Result = false;
if (token.CallBakc != null)
{
token.CallBakc(token);
}
}
}
}
}
finally
{
//还池
GivebackReceiveSocketArgs(e);
}
}
} /// <summary>
/// 异步发送结果
/// </summary>
public class AsyncSendResult
{
/// <summary>
/// 结果
/// </summary>
public bool Result { get; set; } /// <summary>
/// 目标节点
/// </summary>
public string RemoteEndPoint { get; set; } /// <summary>
/// 发送数据
/// </summary>
public byte[] SendData { get; set; } /// <summary>
/// 发送时间
/// </summary>
public DateTime SendTime { get; set; } /// <summary>
/// 结果返回时间
/// </summary>
public DateTime ResultTime { get; set; }
/// <summary>
/// 获取或设置与此操作关联的用户或应用程序对象。
/// </summary>
public object UserToKen { get; set; } /// <summary>
/// 用于回调的委托
/// </summary>
internal Action<AsyncSendResult> CallBakc { get; set; }
}一个服务端只需要一个异步发送[实例 ,
不需要为每一个客户端创建一实例