初学者请教个问题,Winform程序作客户端连接多个服务端进行通讯,C# Socket 也是刚学,没找到合适的书或是资料,看了好多博客写了些,但是一直不能用,想请大伙给看看,提些问题或者建议,感谢的😁
public partial class Form1 : MaterialForm
{
#region 已知信息存有以下信息的DataTable表
//dt表里存的是服务端的IP、端口号、还有发送的内容
DataTable dt_ServerInfo = new DataTable();
#endregion
#region 变量声明
private Dictionary<string, Socket> socketClients = new Dictionary<string, Socket>() { }; //客户端套接字集合
byte[] dataSend = new byte[500];
byte[] ReceiveByte = new byte[1024];
#endregion
#region 线程委托等实例
Thread SendThr = null;
#endregion
public Form1()
{
InitializeComponent();
GenerateDataTable(); //生成dt表结构和内容
CreateSocketConnection(); //创建Socket和建立连接
}
#region 自定义函数
private void CreateSocketConnection()
{
int countOfServers = dt_ServerInfo.Rows.Count;
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()));
serverSocket.BeginConnect(serverEndPoint, new AsyncCallback(ConnectCallBack), serverSocket);
}
}
private void ConnectCallBack(IAsyncResult asyncResult)
{
Socket client = (Socket)asyncResult.AsyncState; //客户端套接字
try
{
client.EndConnect(asyncResult);
socketClients.Add(client.RemoteEndPoint.ToString(), client);
}
catch (Exception) //客户端开启后未连接,自动重连
{
IPEndPoint temppoint = (IPEndPoint)client.RemoteEndPoint;
client.BeginConnect(temppoint, ConnectCallBack, client);
}
}
private void SendThread()
{
while (socketClients.Count() > 0)
{
foreach (var item in socketClients)
{
string serverInfo = item.Key.ToString();
//string tst = $"ServerIp = '{serverInfo.Split(':')[0]}' And ServerPort = '{serverInfo.Split(':')[1]}' ";
DataRow[] drs = dt_ServerInfo.Select($"ServerIp = '{serverInfo.Split(':')[0]}' And ServerPort = '{serverInfo.Split(':')[1]}'");
byte[] sendByte = hexStringToByteArray(drs[0]["Command"].ToString());
item.Value.BeginSend(sendByte, 0, sendByte.Length, SocketFlags.None, new AsyncCallback(SendCallback), item.Value);
}
}
}
private void SendCallback(IAsyncResult asyncResult)
{
Socket client = (Socket)asyncResult.AsyncState; //客户端套接字
try
{
client.EndSend(asyncResult);
client.BeginReceive(ReceiveByte, 0, ReceiveByte.Length, SocketFlags.None, new AsyncCallback(AsyncReceiveCall), client);
}
catch (Exception) //当客户端无法发送命令时,自动断开连接并进行重连
{
IPEndPoint temppoint = (IPEndPoint)client.RemoteEndPoint;
socketClients[temppoint.ToString()].Close();
client.BeginConnect(temppoint, ConnectCallBack, client);
socketClients.Remove(temppoint.ToString());
}
}
private void AsyncReceiveCall(IAsyncResult asyncResult)
{
Socket client = (Socket)asyncResult.AsyncState; //客户端套接字
int bytesRead = client.EndReceive(asyncResult);
MessageBox.Show(ReceiveByte.ToString());
}
这段代码中存在一些潜在的问题,可能会导致程序无法正常工作,以下是一些建议:
1.Dictionary<string, Socket> 可能会导致线程不安全的问题。由于你的应用程序需要在多个线程中操作套接字,因此如果在操作中修改集合,可能会导致线程竞争条件。因此,建议使用 ConcurrentDictionary<string, Socket> 代替 Dictionary<string, Socket> 来确保线程安全。
2.ConnectCallBack() 方法中的异常处理可以更加细致。当前的处理是在发生任何异常时进行自动重连,但这可能会导致无限次的重连尝试。建议对特定的异常类型进行处理,并在重试次数达到一定限制后停止重连。
3.SendThread() 中的 foreach 循环可能会导致性能问题。在此方法中,每个循环迭代都会向套接字发送命令,并在返回时等待异步回调,这可能会导致很多线程被阻塞。可以考虑将发送命令的过程与异步回调分开,并将异步回调委托给另一个线程处理。
4.AsyncReceiveCall() 方法中 MessageBox.Show(ReceiveByte.ToString()) 可能不会正确显示接收到的数据。当前的代码将字节数组转换为字符串并将其显示在消息框中。但这种转换方法可能会导致不可预测的结果,因为字节数组中的每个字节都不一定是有效的 ASCII 字符。建议使用 System.Text.Encoding 类来将字节数组转换为字符串,例如 Encoding.UTF8.GetString(ReceiveByte, 0, bytesRead)。
希望这些建议可以帮助你改进你的程序并解决你遇到的问题。
这个把主要是你找错了方向,某个园子的文章脱离程序届很久了。都是些耍眼球的人自娱自乐的产物
要看代码请去nuget,git。找到程序员们在实践中最新的代码去看,看不懂这些代码在去查。最起码这些代码是新的,而且敢把代码直接show在git的上的也都是有那个自信经的起全世界的coder去coderview的
这个你可以看Rsocket,虽然这个是半成品,而且实现的是Rsocket自己的协议,不过单单就如何处理socket就比大多数园子的博文代码要好太多
项目地址:
你的核心参考代码
https://github.com/rsocket/rsocket-net/blob/master/RSocket.Core/Transports/SocketTransport.cs
我提取一下核心代码
//开始连接
public async Task StartAsync(CancellationToken cancel = default)
{
//检查dns联通性
var dns = await Dns.GetHostEntryAsync(Url.Host);
if (dns.AddressList.Length == 0) { throw new InvalidOperationException($"Unable to resolve address."); }
Endpoint = new IPEndPoint(dns.AddressList[0], Url.Port);
//新建socket
Socket = new Socket(Endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
//连接服务器
Socket.Connect(dns.AddressList, Url.Port); //TODO Would like this to be async... Why so serious???
//连接成功处理,发送和接受
Running = ProcessSocketAsync(Socket);
}
public Task StopAsync() => Task.CompletedTask; //TODO More graceful shutdown
private async Task ProcessSocketAsync(Socket socket)
{
// Begin sending and receiving. Receiving must be started first because ExecuteAsync enables SendAsync.
//启动接收任务
var receiving = StartReceiving(socket);
//启动发送任务
var sending = StartSending(socket);
//等待这两个任务任意一个结束
var trigger = await Task.WhenAny(receiving, sending);
}
这样的代码是不是比那些园子的文章要清楚明白的多。
当然他内部应用了System.io.pipelines做“公共缓存”,用IDuplexPipe做了一个抽象,这个到需要你自己理解
这样你学习完这个代码,就无惧“Tcp,udp,串口,蓝牙”了,因为你最后使用的只是IDuplexPipe,而不是tcp,udp,串口,蓝牙他们的差异在Transports层被统一了