现在给公司做的一个小项目中用到的功能,现在就是本机上功能分开写了2个WINFORM软件,一个服务器,采集现场PLC的数据,并且进行处理存储,并接收客户端的请求,将采集的实时数据传送给客户端,一次完整的数据长度是5500个BYTE宽度,一次socket的发送可以完成的。现在的问题是,打开服务器,然后打开客户端运行没有问题,但是过一段时间(有可能几小时,几天,不定)就会出现客户端连接不上服务器,连接请求拒绝,不知道什么原因导致了服务器的侦听线程失效了。下面贴出源码,各位大神不吝赐教啊,灰常感谢!
服务器的侦听线程的子函数
private void ThreadStartListener()
{
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
DelegateShowTextMessage _D1 = new DelegateShowTextMessage(ShowTextMessage);
try
{
listener.Bind(new IPEndPoint(IPAddress.Any, 11000));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
listener.Listen(50);
while (true)
{
Socket socket = listener.Accept();
//在连接打开后,尝试接收数据时,接收时间超过1秒将引发异常
socket.ReceiveTimeout = 1000;
//在连接打开后,尝试发送数据时,接收时间超过1秒将引发异常
socket.SendTimeout = 1000;
try
{
this.Invoke(_D1, "连接来自 " + ((IPEndPoint)socket.RemoteEndPoint).Address.ToString() + " 端口" + ((IPEndPoint)socket.RemoteEndPoint).Port);
byte[] RByte = new byte[20];
socket.Receive(RByte);
string result = Encoding.ASCII.GetString(RByte).Substring(0,4);
if (result == "1234")
{
byte[] temp = Encoding.ASCII.GetBytes(PlcReadTemp.ToString());
socket.Send(temp);
}
}
catch
{
}
finally
{
socket.Close();
}
}
}
客户端的线程读取函数
private void ThreadReadComputer()
{
DelegatePlcWrong dWrong = new DelegatePlcWrong(PlcWrong);
DelegatePlcRead dRead = new DelegatePlcRead(PlcRead);
IPAddress ip = IPAddress.Parse("127.0.0.1");
IPEndPoint ipend = new IPEndPoint(ip, 11000);
Socket socket;
try
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
catch(Exception ex)
{
this.Invoke(dWrong, ex.Message);
return;
}
try
{
socket.Connect(ipend);
}
catch (Exception ex)
{
this.Invoke(dWrong, ex.Message);
return;
}
byte[] SData;
byte[] RData = new byte[5500];
//Rend D0 to D4 (5 points) with the A-compatible 1E frame command.
//SData = Encoding.ASCII.GetBytes("01FF000A4420000000000500");
//Read D0 to D4 (5 points) with the QnA-compatible 3E frame command.
//SData = Encoding.ASCII.GetBytes("500002FF03E0080018000A04010001D*0001000004");
socket.SendTimeout = 1000;
socket.ReceiveTimeout = 1000;
SData = Encoding.ASCII.GetBytes("1234");
try
{
socket.Send(SData);
socket.Receive(RData);
}
catch
{
socket.Close();
this.Invoke(dWrong, "连接服务器时出现错误");
return;
}
//socket.Shutdown(SocketShutdown.Both);
socket.Close(1);
string str = Encoding.ASCII.GetString(RData);
ClassMy.PlcReadTemp = new StringBuilder(str);
//自己的数据处理
this.Invoke(dRead, str);
//InsertSqlDate(str);
}
------解决思路----------------------
1.建议服务端改为异步accept,去掉while死循环
2.建议显示和逻辑分开,不要使用this.Invoke
------解决思路----------------------
发现了一个最大的问题
Accept和Recieve都在一个过程里
那么假如其中一个客户端,connect之后,一直不发送消息,那你的逻辑就阻塞在Recieve,不会接收其他客户端的连接,也不会接收其他客户端发来的消息
------解决思路----------------------
有几个特征很明显:
1. 在Accept和Receive过程,没有阻塞、循环语句。所以程序代码更简单(只要你理解了异步代码的风格就会觉得很简单)。
2. 每建立一个与客户端的通道,都是一个独立的对象。并且相互不共用 buffer、container 等等对象,相互并发运行而不阻塞。
3. 消息是粘包、分包的(指应用程序的 buffer,而不是针对底层的 buffer 而言)。 你不能随便收到点数据就执行 Encoding.ASCII.GetString(RByte),你必须先能够把 buffer 分割出第一条消息来,才处理。而后边的分包或者粘包的数据也不能乱。