我有几个非常简单的套接字程序来做长度前缀的字符串发送和接收。这是核心例程:
// 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的 发送中... SendStringToSocket:6 SendStringToSocket:发送10 SendStringToSocket:完成 接收... ReadStringFromSocket:阅读...... ReadStringFromSocket:长度:6 ReadStringFromSocket:完成6个字节 响应: 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...
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次。但它会挂起。
有什么想法吗?
答案 0 :(得分:1)
尝试使用Stream类来编写和接收数据,它可能比Socket类更可靠。例如:
TcpClient conn = new TcpClient();
Stream stream; // read and write data to stream
try
{
string msg = "this is a test";
conn.Connect("localhost", 50000);
stream = conn.GetStream();
byte[] by = Encoding.UTF8.GetBytes(msg.ToCharArray(), 0, msg.Length);
await stream.WriteAsync(by, 0, by.Length); // write bytes to buffer
stream.Flush(); // send bytes, clear buffer
by = new byte[2048]; // new byte array to store received data
//wait until buffer has data, then returns buffer length
int bytesAvailable = await stream.ReadAsync(by, 0, 2048);
msg = Encoding.UTF8.GetString(by, 0, bytesAvailable);
} catch (Exception e) { //output exception
}
这就是我为Win 8 ui app所做的。因此,您可能会使用常规的流读取和写入方法,在调用之前不需要等待。即。
stream.Write(by, 0, by.Length);
stream.Read(by, 0, 2048);
顺便说一句,如果您在Win 8商店应用程序中执行此操作,则需要一个Stream对象进行阅读,另一个用于写入。
答案 1 :(得分:0)
我能够验证客户端和服务器 - 代码被剥离回上面发布的更简单的版本 - 当东海岸客户端点击西海岸服务器时,对于大量的交互完全正常。必须是我本地网络中的东西。不是软件问题。感谢大家的见解。