【发布时间】:2014-03-05 15:50:25
【问题描述】:
我有几个非常简单的套接字程序来发送和接收以长度为前缀的字符串。以下是核心例程:
// Send a string, length-prefixed, to a socket.
public static void SendStringToSocket(Socket socket, string str)
{
byte[] dataBuffer = Encoding.UTF8.GetBytes(str);
Console.WriteLine("SendStringToSocket: " + dataBuffer.Length);
byte[] lengthBuffer = LengthToNetworkBytes(dataBuffer.Length);
byte[] overallBuffer = new byte[dataBuffer.Length + lengthBuffer.Length];
for (int b = 0; b < lengthBuffer.Length; ++b)
overallBuffer[b] = lengthBuffer[b];
for (int d = 0; d < dataBuffer.Length; ++d)
overallBuffer[d + lengthBuffer.Length] = dataBuffer[d];
Console.WriteLine("SendStringToSocket: Sending " + overallBuffer.Length);
socket.Send(overallBuffer);
Console.WriteLine("SendStringToSocket: Complete");
}
// Read a length-prefixed string from a socket.
public static string ReadStringFromSocket(Socket socket)
{
byte[] buffer = new byte[8192];
bool bReadLength = false;
int nStrLen = -1;
MemoryStream memStream = new MemoryStream(buffer.Length);
while (true)
{
Console.WriteLine("ReadStringFromSocket: Reading...");
int nRead = socket.Receive(buffer, SocketFlags.None);
if (nRead == 0)
break;
int nOffset = 0;
if (!bReadLength)
{
byte[] lenBuffer = new byte[sizeof(int)];
if (nRead < lenBuffer.Length)
throw new RuntimeException(ErrorCode.NetworkError, "Reading string length failed.");
for (int b = 0; b < lenBuffer.Length; ++b)
lenBuffer[b] = buffer[b];
nStrLen = NetworkBytesToLength(lenBuffer);
Console.WriteLine("ReadStringFromSocket: Length: " + nStrLen);
if (nStrLen < 0)
throw new RuntimeException(ErrorCode.NetworkError, "Invalid string length: " + nStrLen + " - be sure to convert from host to network");
bReadLength = true;
nOffset = lenBuffer.Length;
if (nStrLen == 0)
{
Console.WriteLine("ReadStringFromSocket: Complete with no length");
if (nRead != lenBuffer.Length)
throw new RuntimeException(ErrorCode.NetworkError, "Zero length string has more data sent than expected.");
return "";
}
}
memStream.Write(buffer, nOffset, nRead - nOffset);
if (memStream.Length > nStrLen)
throw new RuntimeException(ErrorCode.NetworkError, "More string data sent than expected.");
if (memStream.Length == nStrLen)
break;
}
Console.WriteLine("ReadStringFromSocket: Complete with " + memStream.Length + " bytes");
return Encoding.UTF8.GetString(memStream.GetBuffer(), 0, (int)memStream.Length);
}
我认为这些例程体现了这类事情的最佳实践。
这是客户端应用程序:
static void Main(string[] args)
{
if (args.Length != 2)
{
Console.WriteLine("StringSocketClient <server address> <server port>");
return;
}
TcpClient client = new TcpClient(args[0], int.Parse(args[1]));
Socket socket = client.Client;
while (true)
{
Console.Write("> ");
string strInput = Console.ReadLine();
Console.WriteLine("Sending...");
Utils.SendStringToSocket(socket, strInput);
Console.WriteLine("Receiving...");
string strResponse = Utils.ReadStringFromSocket(socket);
Console.WriteLine("Response:");
Console.WriteLine(strResponse);
Console.WriteLine();
}
}
这是服务器应用程序:
static void Main(string[] args)
{
if (args.Length != 1)
{
Console.WriteLine("StringSocketEchoServer <TCP port to serve>");
return;
}
TcpListener listener = new TcpListener(IPAddress.Any, int.Parse(args[0]));
listener.Start();
while (true)
{
TcpClient client = listener.AcceptTcpClient();
new Thread(ProcessConnection).Start(client);
}
}
static void ProcessConnection(object state)
{
TcpClient client = (TcpClient)state;
Socket socket = client.Client;
try
{
while (true)
{
Console.WriteLine("Reading from network...");
string str = Utils.ReadStringFromSocket(socket);
Console.WriteLine("Received: " + str);
Console.WriteLine("Sending back...");
Utils.SendStringToSocket(socket, str);
}
}
catch (Exception exp)
{
Console.WriteLine("EXCEPTION!\r\n" + exp);
try
{
client.Close();
client = null;
}
catch { }
}
}
真正直截了当、简单的东西,切入问题的根源。
我发现如果我进行五次交互,客户端会挂起以尝试接收响应,而服务器正在尝试接收请求。这是控制台输出:
客户:
foobar 发送... 发送字符串到套接字:6 SendStringToSocket: 发送 10 SendStringToSocket:完成 接收... ReadStringFromSocket:正在读取... ReadStringFromSocket:长度:6 ReadStringFromSocket:6 个字节完成 回复: 富吧
> foobar
Sending...
SendStringToSocket: 6
SendStringToSocket: Sending 10
SendStringToSocket: Complete
Receiving...
ReadStringFromSocket: Reading...
ReadStringFromSocket: Length: 6
ReadStringFromSocket: Complete with 6 bytes
Response:
foobar
> foobar
Sending...
SendStringToSocket: 6
SendStringToSocket: Sending 10
SendStringToSocket: Complete
Receiving...
ReadStringFromSocket: Reading...
ReadStringFromSocket: Length: 6
ReadStringFromSocket: Complete with 6 bytes
Response:
foobar
> foobar
Sending...
SendStringToSocket: 6
SendStringToSocket: Sending 10
SendStringToSocket: Complete
Receiving...
ReadStringFromSocket: Reading...
ReadStringFromSocket: Length: 6
ReadStringFromSocket: Complete with 6 bytes
Response:
foobar
> foobar
Sending...
SendStringToSocket: 6
SendStringToSocket: Sending 10
SendStringToSocket: Complete
Receiving...
ReadStringFromSocket: Reading...
^CPress any key to continue . . .
服务器:
Reading from network...
ReadStringFromSocket: Reading...
Reading from network...
ReadStringFromSocket: Reading...
ReadStringFromSocket: Length: 6
ReadStringFromSocket: Complete with 6 bytes
Received: foobar
Sending back...
SendStringToSocket: 6
SendStringToSocket: Sending 10
SendStringToSocket: Complete
Reading from network...
ReadStringFromSocket: Reading...
ReadStringFromSocket: Length: 6
ReadStringFromSocket: Complete with 6 bytes
Received: foobar
Sending back...
SendStringToSocket: 6
SendStringToSocket: Sending 10
SendStringToSocket: Complete
Reading from network...
ReadStringFromSocket: Reading...
ReadStringFromSocket: Length: 6
ReadStringFromSocket: Complete with 6 bytes
Received: foobar
Sending back...
SendStringToSocket: 6
SendStringToSocket: Sending 10
SendStringToSocket: Complete
Reading from network...
ReadStringFromSocket: Reading...
ReadStringFromSocket: Length: 6
ReadStringFromSocket: Complete with 6 bytes
Received: foobar
Sending back...
SendStringToSocket: 6
SendStringToSocket: Sending 10
SendStringToSocket: Complete
Reading from network...
ReadStringFromSocket: Reading...
当天早些时候,是三个互动,然后是挂起。
我认为代码是正确的。它工作X次。但随后它挂起。
有什么想法吗?
【问题讨论】:
-
对混乱的客户端:/服务器:输出感到抱歉。让我知道您是否需要在上面倾倒的内容之外进行澄清。
-
从客户端:/服务器:输出可能不清楚,但在一天结束时,客户端认为发送正常并继续尝试接收服务器响应,但是服务器只是停留在接收请求,就好像客户端发送的数据被卡住或被丢弃一样。
-
客户在我的办公室。服务器位于 AWS EC2 上。我尝试使用不同的 AWS EC2 禁用我的本地安全软件,禁用服务器上的 Windows 防火墙。那奇怪的五次......
-
您有几个对象实现了
IDisposable,但它们不在using块中。至少您的MemoryStream、TcpClient和Socket实例。未实现using块的一个常见症状是操作“在一段时间后”失败。 -
感谢您的浏览。我不认为测试应用程序需要使用块......我不明白添加使用块会如何改变我面临的问题......这不是资源泄漏,而是网络问题。另一个线索:Wireshark 在挂起的情况下说 TCP Retransmission 很多次。
标签: c# sockets tcp client-server tcpclient