Reputation: 339
I have a couple very simple socket programs to do length-prefixed string sending and receiving. Here's the core routines:
// 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);
}
I think these routines embody the best practices for this sort of thing.
Here's the client app:
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();
}
}
Here's the server app:
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 { }
}
}
Real straightforward, simple stuff, cut down to the bone of the problem.
What I'm finding is that if I do five interactions, the client hangs trying to receive a response, and the server is trying to receive a request. Here's the console outputs:
Client:
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...
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 . . .
Server:
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...
Earlier in the day, it was three interactions then the hang.
I think the code's correct. It works X times. But then it hangs.
Any ideas?
Upvotes: 1
Views: 14414
Reputation: 339
I was able to verify that the client and server - with code stripped back to even simpler versions that posted above - interact perfectly fine for lots and lots of interactions when an East Coast client hits a West Coast server. Must be something in my local networking. Not a software problem. Thanks to everybody for your insights.
Upvotes: 0
Reputation: 784
Try using the Stream class to write and receive data, it might be more reliable then the Socket class. example:
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
}
This is what I did for a Win 8 ui app. Because of that, you could probably get away with the regular Read and Write methods of stream, which do not need await before calling. i.e.
stream.Write(by, 0, by.Length);
stream.Read(by, 0, 2048);
By the way, if you ever do this in Win 8 store app, you need a Stream object for reading and another for writing.
Upvotes: 1