tags:

views:

100

answers:

1

I am creating a socket server in C# and a client in PHP. The conversation between client and server is something like this:

  1. client connects
  2. server sends welcome message
  3. client sends data
  4. server sends response
  5. (repeat step 3 - 4 until client disconnects)

It works until step 3. The data from the client is received by the server. However, the client waits forever until the server sends its response and eventually times out.

Here is the relevant C# code:

class Program
{
    private const int CONNECT_QUEUE_LENGTH = 4;

    static void ListenForRequests()
    {
        Socket listenSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        listenSock.Bind(new IPEndPoint(IPAddress.Any, 9999));
        listenSock.Listen(CONNECT_QUEUE_LENGTH);
        listenSock.Blocking = true;

        byte[] data = new byte[1024];

        while (true)
        {
            Socket newConnection = listenSock.Accept();
            newConnection.Blocking = true;

            // Create new thread for each request
            var t = new Thread(() => new RequestHandler(newConnection));
            t.Start();
        }
    }
}

class RequestHandler
{
    public RequestHandler(Socket socket)
    {
        Util.WriteToSocket("A message", socket);
        Console.WriteLine("Received: " + Util.ReadSocketToEnd(socket).Length);
        Util.WriteToSocket("Fin!", socket);
    }
}    

static class Util
{ 
    public static string ReadSocketToEnd(Socket newConnection)
    {
        var sb = new StringBuilder();

        byte[] data = new byte[1024];
        int receivedDataLength = newConnection.Receive(data);

        while (receivedDataLength > 0)
        {
            try
            {
                sb.Append(Encoding.UTF8.GetString(data, 0, receivedDataLength));
                receivedDataLength = newConnection.Receive(data);
            }
            catch (SocketException)
            {
                break;
            }
        }

        return sb.ToString();
    }

    public static void WriteToSocket(string message, Socket client)
    {
        byte[] data = Encoding.UTF8.GetBytes(message);
        client.Send(data, SocketFlags.None);
    }
}

And here is the simple PHP client, without any error handling etc:

<?php

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$result = socket_connect($socket, '127.0.0.1', 9999);

print 'receiving: ' . chr(10);
print socket_read ($socket, 10) . chr(10);
print 'receive end' . chr(10);


$message = str_repeat("Message to sent", 1000);
socket_write($socket, $message, strlen($message));
print 'message sent' . chr(10);


print 'reading fin' . chr(10); // This is printed, but a response is never received
print socket_read ($socket, 10) . chr(10);
print 'fin read!' . chr(10);

socket_shutdown($socket);
+4  A: 

You're reading from the socket until all the data has been read - meaning "all the data until the socket is closed". Your PHP client is writing data, but not closing the socket - so the server is waiting forever.

TCP sockets provide a stream of data. There's no concept of "the end of the current message" which is what it looks like you want.

The three ways of handling this are usually:

  • A message delimiter (e.g. a line break)
  • A message length prefix (number of bytes in the message just before the message itself)
  • Use a different connection for each request/response pair

You could potentially use a read with a timeout, and assume that if you haven't seen any more data in the last 5 seconds, you've got the whole message - but that's fragile, inefficient and generally a bad idea.

Jon Skeet
That explains a lot! How would I implement a method that reads from the socket until a certain delimeter (let it be newline \n or something else ) is reached?
Max
@Max: It's not particularly easy, because you might end up with more data than you want to consume, so you'll have to store that somewhere. In principal though, just wrap the socket's stream in a StreamReader and keep calling `Read`, then look through the buffer to see if that character you're interested in is present. It's more complicated when you have a multi-character delimiter of course...
Jon Skeet
Thanks a lot Jon for your explanation. That clarified some strange behaviour of my socket server and client.
Max