我正在尝试优化一个tcp套接字包装器,它正在努力解决许多入站连接问题。我正在基本的聊天服务器和小客户端应用程序中测试它,以便将客户端发送给它。这两个应用程序都位于通过千兆交换机连接的单独W2k3服务器上。
通过反复试验,我将我的测试改进为以100毫秒间隔连接的10个客户端,然后一旦所有10个连接,他们每个都向服务器发送一个“输入房间”消息,再次以100毫秒的间隔。当服务器收到一条消息时,它会向发件人回复房间里的每个人的列表,并向房间里的其他人发送一条消息,说明是新来的。
每次发送都需要1秒钟才能完成(100个客户端需要3-4秒),并且通过日志记录我已确定延迟是在Socket.SendAync和相应的事件被引发之间。整个CPU使用率仍然很低。
我已经尝试了所有我能想到的东西,花了几天时间在线寻找线索,我完全失去了。这不能正常吗?
编辑:按要求编码。我已经整理了一下,删除了不相关的计数器和日志记录等,当我试图缩小问题范围时,它目前已经破解了黑客攻击之上的kludge。
private void DoSend(AsyncUserToken token, String msg)
{
SocketAsyncEventArgs writeEventArgs = new SocketAsyncEventArgs();
writeEventArgs.Completed += ProcessSend;
writeEventArgs.UserToken = token;
Byte[] sendBuffer = Encoding.UTF8.GetBytes(msg + LineTerminator);
writeEventArgs.SetBuffer(sendBuffer, 0, sendBuffer.Length);
Interlocked.Add(ref m_totalBytesAttemptedSend, sendBuffer.Length);
Logger2.log(Logger2.Debug5, token.ConnectionId, "Total Bytes attempted: " + m_totalBytesAttemptedSend);
bool willRaiseEvent = true;
try
{
willRaiseEvent = token.Socket.SendAsync(writeEventArgs);
}
catch (Exception e)
{
Logger2.log(Logger2.Debug2, token.ConnectionId, e.Message);
writeEventArgs.Dispose();
}
if (!willRaiseEvent)
{
ProcessSend(null, writeEventArgs);
}
}
private void ProcessSend(Object sender, SocketAsyncEventArgs e)
{
AsyncUserToken token = (AsyncUserToken)e.UserToken;
Logger2.log(Logger2.Debug5, token.ConnectionId, "Send Complete");
if (e.SocketError == SocketError.Success)
{
Interlocked.Add(ref m_totalBytesSent, e.BytesTransferred);
Logger2.log(Logger2.Debug5, ((AsyncUserToken)e.UserToken).ConnectionId, "Total Bytes sent: " + m_totalBytesSent);
}
else
{
if (token.Connected)
{
CloseClientSocket(token);
}
}
e.Dispose();
}
答案 0 :(得分:1)
您在每个连接上发送了多少数据以及发送速度有多快?
通常异步发送需要很长时间才能完成的原因是你已填满TCP窗口(详见here),本地TCP堆栈在获得一些ACK之前无法再发送任何数据来自同行。如果您继续发送数据,那么您只是在网络子系统中本地排队,因为不允许堆栈发送它。拥塞和数据包丢失会使情况变得更糟,因为传输中的数据需要更长时间才能到达对等端,并且ACK需要更长时间才能恢复...
如果是这种情况(并且诸如WireShark之类的工具应该使您能够看到窗口大小更新,零窗口情况等),那么增加连接的窗口大小可能有所帮助(参见here)。 / p>
如果您的协议没有明确的流量控制,则更有可能发生上述情况。最好在协议设计中包含某种形式的显式流控制,恕我直言,以避免这种情况。
由于无限发送者可以非常快速地咀嚼内存,因此包含一些发送方保护也是明智的。我使用过的效果很好的一种方法是建立一个可以放入出站数据的队列,并根据早期发送的发送完成情况实际发送此队列的数据(参见here)。
答案 1 :(得分:1)
尝试使用socket.NoDelay=True;
。
默认值为No
,这意味着底层堆栈会尝试在实际发送之前累积传输。