参考内容:
最近需要写个网口通信的上位机程序,我负责写客户端,控制多个客户端与多个服务端简历连接进行通讯,关于异常处理一直有问题,写出来做个记录。
建立连接
/// <summary>
/// 与客户端建立连接:若出错,则开辟一个新线程,在新线程里每隔五秒尝试连接一次,连接成功的话跳出循环,加入到列表中
/// </summary>
private void CreateSocketConnection()
{
int countOfServers = dt_ServerInfo.Rows.Count; //dt表中存着服务端的IP地址和端口号
for (int i = 0; i < countOfServers; i++)
{
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse(dt_ServerInfo.Rows[i]["serverIP"].ToString()),
int.Parse(dt_ServerInfo.Rows[i]["serverPort"].ToString()));
try
{
IAsyncResult result = serverSocket.BeginConnect(serverEndPoint, null, null);
result.AsyncWaitHandle.WaitOne(500);
clientsockets.Add(serverSocket);
socketClients.TryAdd(serverSocket.RemoteEndPoint.ToString(), serverSocket);
}
catch (SocketException) //尝试访问套接字时出错
{
Thread thr_connect = new Thread(() =>
{
try
{
for (int j = 0; j < 10; j++)
{
//serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Connect(serverEndPoint);
DateTime now = DateTime.Now;
while (now.AddSeconds(5) > DateTime.Now) { }
break;
}
clientsockets.Add(serverSocket);
socketClients.TryAdd(serverSocket.RemoteEndPoint.ToString(), serverSocket);
}
catch { }
});
thr_connect.IsBackground = true;
thr_connect.Start();
}
}
}
发送数据
开辟一个线程来专门发送数据:
/*全局变量*/
Thread Thr_Send = null;
volatile bool Stopflag = false; //发送停止标志位 - volatile保证不被优化掉
......
/*初始化*/
Thr_Send = new Thread(new ThreadStart(SendThread));
Thr_Send.IsBackground = true;
Thr_Send.Start();
/*发送线程内容*/
private void SendThread()
{
while (true)
{
if (!Stopflag)
{
try
{
foreach (var item in socketClients)
{
string serverInfo = item.Key.ToString();
DataRow[] drs = dt_ServerInfo.Select($"ServerIp = '{serverInfo.Split(':')[0]}' And ServerPort = '{serverInfo.Split(':')[1]}'");
byte[] sendByte = hexStringToByteArray(drs[0]["Command"].ToString());
try
{
IAsyncResult result = item.Value.BeginSend(sendByte, 0, sendByte.Length, SocketFlags.None, null, null);
result.AsyncWaitHandle.WaitOne(500);
}
catch (SocketException) // 尝试访问套接字时出错
{
IPEndPoint tempEndpoint = (IPEndPoint)item.Value.RemoteEndPoint;
item.Value.Shutdown(SocketShutdown.Both);
item.Value.Disconnect(true);
item.Value.Close();
Socket tempsocket = item.Value;
socketClients.TryRemove(item.Key, out tempsocket);
Thread thr_reconnect = new Thread(() =>
{
try
{
int j = 0;
for (; j < 10; j++)
{
tempsocket.Connect(tempEndpoint);
DateTime now_temp = DateTime.Now;
while (now_temp.AddSeconds(5) > DateTime.Now) { }
break;
}
if (j!=10)
{
socketClients.TryAdd(tempsocket.RemoteEndPoint.ToString(), tempsocket);
}
Thread.CurrentThread.Abort();
}
catch { }
});
thr_reconnect.IsBackground = true;
thr_reconnect.Start();
}
}
DateTime now = DateTime.Now;
while (now.AddSeconds(1) > DateTime.Now) { }
}
catch (Exception ex)
{
throw ex;
}
}
}
}
BeginReceive
也是这么写的,就不贴了,我这只处理了客户端程序错误,没有处理上面说的物理问题,我先测测能用不。
心跳检测
这个不会写,不知道对不对
新开了一个线程:
private byte[] GetKeepLiveData()
{
uint dummy = 0;
byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);
BitConverter.GetBytes((uint)3000).CopyTo(inOptionValues, Marshal.SizeOf(dummy)); //keep-alive 间隔
BitConverter.GetBytes((uint)500).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2); //尝试间隔
return inOptionValues;
}
private void CheckAlive()
{
Thread.Sleep(10000);
while (true)
{
try
{
lock (socketClients)
{
foreach (var item in socketClients)
{
//if (item.Client.Client.Poll(500, System.Net.Sockets.SelectMode.SelectRead) && (item.Client.Client.Available == 0))
if (item.Value.Poll(500, System.Net.Sockets.SelectMode.SelectRead) && item.Value.Available == 0)
{
//MaterialMessageBox.Show("未收到心跳检测回复");
//心跳检测处理
item.Value.Shutdown(SocketShutdown.Both);
item.Value.Disconnect(true);
item.Value.Close();
Socket tempsocket = item.Value;
socketClients.TryRemove(item.Key, out tempsocket);
}
}
}
}
catch (Exception e)
{
MaterialMessageBox.Show(e.ToString());
}
Thread.Sleep(500);
}
}