服务器接收信息并 Socket.send 发送回值 会有堵塞问题 求大神帮解决下

hjn0212 2018-08-15 12:30:38
现在有一个服务器端 多线程 监听端口 等待客户端连接 连接上来之后 客户端向服务器端每1分钟发送一次数据 服务器端每收到一条数据 需要给客户端回值一个特定字符串 表示收到了 问题是 当服务器用端用 Socket.send发送数据时 有时候会卡在这里 影响到了服务器端正常接收客户端数据 如果再加上客户端信号不好 那时间更长 请问如何解决? 我贴出代码

Socket sokClient = sokConnectionparn as Socket;

while (true)
{
// 定义一个1M的缓存区;
byte[] arrMsgRec = new byte[1024];
// 将接受到的数据存入到输入 arrMsgRec中;
int length =0;
try
{
length = sokClient.Receive(arrMsgRec, 0, 1024, SocketFlags.None);// 接收数据,并返回数据的长度;
if (length > 0)
{
string strMsg = System.Text.Encoding.ASCII.GetString(arrMsgRec).TrimEnd('\0');// 将接受到的字节数据转化成字符串;
//给客户端回值
string ip=sokClient.RemoteEndPoint.ToString();
dict[ip].Send(“OOKK”);
}
}
}
...全文
1058 29 打赏 收藏 转发到动态 举报
写回复
用AI写文章
29 条回复
切换为时间正序
请发表友善的回复…
发表回复
drifter2002 2018-08-19
  • 打赏
  • 举报
回复
新建一个后台线程,处理数据的首发
liulilittle 2018-08-16
  • 打赏
  • 举报
回复
你为什么要用同步的方式?本机环境下用 send 的确会比异步快(对方收取的速度快时)
但脱离本机环境的情况,一个小型的局域网里面两者效率差距并不大,但大多情况下异
步的效率会高过同步,放到广域网异步效率一定大于同步。

send 发送数据报文,首先从应用层(RING3)开始向下传递WSABUF到“表示层(winsock)”,
继续向下传递到“网络分层结构(网络会话层 / mswsock)”中,此时经过分层处理后,
断入内核,传送到“afd.sys ”内核层中,内核首先“拷贝”需要发送的 wsabuf 到对应
的 “tcp_pcb”结构中的 “sendwnd (发送窗口缓冲区)”由内核决定何时发送

但有一个很有趣的现象就是网卡环路,操作系统一般会hook掉向“127.0.0.1=localhost”、
“各个网卡 Ether IPEP”的流量出入,所以遇见这些情况它几乎不会调用网卡向外真正发
送任何数据包,而是内核层直接桥接数据包到对应的 fd 上面。(所以它是不存在延迟的,
除开内核拷贝与滑块窗口之间的性能开销)

【winnt/linux 都是 BSD/socket 协议栈的实现】

然后 socket 各种设置,例如:nagle 算法(nodelay)的设置影响会比较大(它会控制内核粘包的情况)
一个很直白的例子就是说,send 函数的成功必须目标主机接收到发送的数据同时内核收取到目标主机的
ACK 才会“发射事件信号 * NtSetEvent”同时 send 函数停止阻塞。

但你真的认为,send 函数返回的正确值就一定是发送数据的长度?返回 0 或者不足发送长度的情况几乎
无时无刻都在发生,唯一确认 send 发送失败的情况就是返回 SOCKET_ERROR(~0)。

若目标主机迟迟不发送 ACK,那么 send 将被无限的阻塞,但 tcp-net-stack 显然不允许这个情况所以
在一定的时间后,无法的到 ACK,那么连接将发生错误,在内核中收取到来自目标主机 PSH 的 tcp-segments
将全部被丢弃。

但目标主机的提供的 ACK 可能会引发多个 send 函数“内核事件”的触发,前面提到了“send”函数
发送数据并不是立即发送的,而是经过多层的处理,最后拷贝到内核的socketpoll-tcppcb 结构的发送缓冲区中
何时发送不是由“应用层”决定的,而是由“内核的 socketpoll”与其“时钟周期”决定的。

无论应用层是否使用“nagle”算法,都仅仅只是影响 “核 socketpoll”对这个 fd 的发送频率而已(影响了发送频率,
高频率的应用层要求发送数据包,就可以在滑动窗口中可以组成更大的 tcp-segments 组合起来【在网络延迟很大的情
况下会大大的提高传输的效率,虽然增大了 rx_srtt*(平滑往返时间,俗称平滑网络延迟)】)

但它不可能避免 tcp 粘包的可能,只要 tcp 协议栈继续采取 滑动窗口协议 那么就无法避免“会把几个send”的数据
粘在一次向目标主机发送,所以对方只要 ACK 时(ACK 发送方 PSH 的流水号 + PSH 数据长度)时必然会引发多个向
应用层的 send 的 “内核时间” *Set。

但事实情是若应用层向目标主机 send 数据包,必须要等待对方回复才释放 send 的阻塞,那么网络的效能是无法得到
提高的,所以操作系统一般会允许 send 发送的数据长度,几乎濒临到一个曲值的时候(主要是发送窗口大小与 sock设
置 所决定)然后会将其逐步的向外发送(这就是为什么网卡上面会一瞬间 PSH 很多到目标主机的数据包,但等待一定周
期以后目标主机向当前主机ACK以后,当前主机继续爆发式的向目标主机 PSH 数据)。

注:“滑动窗口协议”是用于控制发送速度的(影响到应用层)
游北亮 2018-08-16
  • 打赏
  • 举报
回复
你也可以直接使用Socket提供的异步机制:

listenSocket.BeginAccept(ar =>
{
var client = listenSocket.EndAccept(ar);
client.Send(Encoding.UTF8.GetBytes("Hello world"));
}, null);

游北亮 2018-08-16
  • 打赏
  • 举报
回复
特意去翻了一下2012年写的代码,先Accept,然后交给线程处理响应,就不会堵塞了:

ThreadPool.SetMinThreads(200, 50);

// 监听的代码
Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint serverInfo = new IPEndPoint(IPAddress.Parse("0.0.0.0"), MyConfigHelper.ListenPort);
listenSocket.Bind(serverInfo); //将SOCKET接口和IP端口绑定
listenSocket.Listen(MyConfigHelper.SocketAcceptNum); //开始监听,并且指定队列中最多可容纳的等待接受的传入连接数
LogHelper.WriteCustom("listening on port " + MyConfigHelper.ListenPort, "socketStart\\", false);

while (true)
{
try
{
Socket socket = listenSocket.Accept(); // 接受一个客户端

// IsBackground是避免exe退出,线程不会自动终止
// new Thread(RecieveAccept) { IsBackground = true }.Start(socket);
// 不能用线程池,因为默认线程池个数最小值是处理器数(24),导致线程很快被消耗一空,
// 新Socket连接无法在2秒内进入方法处理并返回,导致客户端超时
// 也可以使用 ThreadPool.SetMinThreads 来把初始值调大
ThreadPool.UnsafeQueueUserWorkItem(RecieveAccept, socket);
}
catch (Exception ex)
{
LogHelper.WriteException("err listening: ", ex);
}
}


雪狼孤竹 2018-08-16
  • 打赏
  • 举报
回复
既然需要发送回复,你需要解决的问题是,客户端收到两条及两条以上数据的处理。
网络不可测,这种情况一定会发生,即使你上面的不卡,你同样会收到多条数据。
xian_wwq 2018-08-16
  • 打赏
  • 举报
回复
引用 楼主 hjn0212 的回复:
现在有一个服务器端 多线程 监听端口 等待客户端连接 连接上来之后 客户端向服务器端每1分钟发送一次数据 服务器端每收到一条数据 需要给客户端回值一个特定字符串 表示收到了 问题是 当服务器用端用 Socket.send发送数据时 有时候会卡在这里 影响到了服务器端正常接收客户端数据 如果再加上客户端信号不好 那时间更长 请问如何解决? 我贴出代码

Socket sokClient = sokConnectionparn as Socket;

while (true)
{
// 定义一个1M的缓存区;
byte[] arrMsgRec = new byte[1024];
// 将接受到的数据存入到输入 arrMsgRec中;
int length =0;
try
{
length = sokClient.Receive(arrMsgRec, 0, 1024, SocketFlags.None);// 接收数据,并返回数据的长度;
if (length > 0)
{
string strMsg = System.Text.Encoding.ASCII.GetString(arrMsgRec).TrimEnd('\0');// 将接受到的字节数据转化成字符串;
//给客户端回值
string ip=sokClient.RemoteEndPoint.ToString();
dict[ip].Send(“OOKK”);
}
}
}


1.数据接收和解析需要分离。
这样可以避免由于数据处理时间过长影响到数据通讯环节。
一个客户端起一个线程的模型少量可以,超过一定量就不推荐了。
可以参考这一篇
https://blog.csdn.net/tpriwwq/article/details/38032547

2.网络通讯对异常的处理非常重要
客户端:读写超时后应主动断开连接,重新发起请求;
服务端:应该有超时检测,如果某一路Client超过一段时间未发送数据,则应该断开socket,清理相关资源;
避免资源耗尽。
3.除非必须,为了简化处理,推荐使用短连接。

4.lz说的卡死不知道是怎么测试的
多线程代码调试不能靠下断点,因为下断点会影响到多个线程
推荐使用打印日志来调试


smwhotjay 2018-08-15
  • 打赏
  • 举报
回复
select io 模型
xuzuning 2018-08-15
  • 打赏
  • 举报
回复
你已经开了线程用于接收数据,那么在接收完成后,为何不能继续用它发送数据?
再者说,就算是专司接收,那么接收完了,线程也就结束了。再开一个发送线程又有何不可?

有理不在声高,谁也不是吓大的。1万个线程 有如何?只要你的硬件能够支持,十万个又能怎样?
hjn0212 2018-08-15
  • 打赏
  • 举报
回复
引用 18 楼 xuzuning 的回复:
给他个线程 专门处理接收他过来的数据
给他个线程 专门处理接收他过来的数据,并且完成后续放入应答,直到客户关闭连接

如果我又1万个客户端 连接 我目前是开1万个线程 来处理收到的数据 再开1万个线程 专门处理这些回值?
hjn0212 2018-08-15
  • 打赏
  • 举报
回复
引用 18 楼 xuzuning 的回复:
给他个线程 专门处理接收他过来的数据
给他个线程 专门处理接收他过来的数据,并且完成后续放入应答,直到客户关闭连接

那意思是在目前的基础上 再起一批同数量的线程?
xuzuning 2018-08-15
  • 打赏
  • 举报
回复
给他个线程 专门处理接收他过来的数据
给他个线程 专门处理接收他过来的数据,并且完成后续放入应答,直到客户关闭连接
hjn0212 2018-08-15
  • 打赏
  • 举报
回复
引用 16 楼 xuzuning 的回复:
无法理解你的:服务器端 多线程 监听端口 是什么意思

说反了 就是服务器端 监听某个端口 客户上来了 就给他个线程 专门处理接收他过来的数据
你的意思是说 我再开新线程 再去处理回传的事? 处理完了 再把这线程关了? 如此重复?
xuzuning 2018-08-15
  • 打赏
  • 举报
回复
无法理解你的:服务器端 多线程 监听端口 是什么意思
Nick黄 2018-08-15
  • 打赏
  • 举报
回复
用UDP消息吧,这个不会堵。
xuzuning 2018-08-15
  • 打赏
  • 举报
回复
收到客户端连接请求后,开启一个子线程,专司接收数据及之后与该客户端的数据交换事宜
因为可能有多个客户端同时发起连接,你不可能也不应该让其他客户端去等待一个客户端的通讯完成
当然,这就需要你调整程序架构,而不是局限在一对一的教学代码上
sdfgrtyu 2018-08-15
  • 打赏
  • 举报
回复
那你的意思是用sendasync方法了
hjn0212 2018-08-15
  • 打赏
  • 举报
回复
有没有能给解决一下子的啊?
hjn0212 2018-08-15
  • 打赏
  • 举报
回复
引用 10 楼 Snowwolf_119 的回复:
[quote=引用 9 楼 hjn0212 的回复:]
网络这事无法左右 就是只按照 客户端能正常的每隔1分钟传过来1条数据处理 如果有上万个客户端每个1分钟传过来一条数据 Socket.send 就会出现我描述的那种状态 现在是想 能怎么做 这2块能不干扰到 客户端还是1分钟传一次 我该接收接收 该入库入库 接收数据之后 我就给他回传1个OOKK 也不需要考虑他收没收到


如果说不考虑客户端收没收到,个人觉得你这个回复,就没有必要存在了。
只做好接收就可以了。
[/quote]
这个咱别讨论了 没意义 现在需要做的就是 需要给他回复
雪狼孤竹 2018-08-15
  • 打赏
  • 举报
回复
引用 9 楼 hjn0212 的回复:
网络这事无法左右 就是只按照 客户端能正常的每隔1分钟传过来1条数据处理 如果有上万个客户端每个1分钟传过来一条数据 Socket.send 就会出现我描述的那种状态 现在是想 能怎么做 这2块能不干扰到 客户端还是1分钟传一次 我该接收接收 该入库入库 接收数据之后 我就给他回传1个OOKK 也不需要考虑他收没收到


如果说不考虑客户端收没收到,个人觉得你这个回复,就没有必要存在了。
只做好接收就可以了。
加载更多回复(9)

110,533

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧