0
votes

According to MSDN I have following code to create Client/Server communication with TCP/IP StreamSockets.

Server:

public async void Start(int port)
{
    try
    {
        //Create a StreamSocketListener to start listening for TCP connections.
        StreamSocketListener socketListener = new StreamSocketListener();

        //Hook up an event handler to call when connections are received.
        socketListener.ConnectionReceived += SocketListener_ConnectionReceived;

        //Start listening for incoming TCP connections on the specified port. You can specify any port that's not currently in use.
        await socketListener.BindServiceNameAsync(port.ToString());
    }
    catch (Exception ex)
    {
        Debug.WriteLine("SocketServer could not start. " + ex.ToString());
    }
}

private async void SocketListener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
    try
    {
        string request;

        //Read line from the remote client.
        using (Stream inStream = args.Socket.InputStream.AsStreamForRead())
        {
            using (StreamReader reader = new StreamReader(inStream))
            {
                request = await reader.ReadLineAsync();
            }
        }

        // Process command and get response
        SocketResponse response = ProcessRequest(request);
        string responseString = JsonHelper.ToJson<SocketResponse>(response);

        //Send the line back to the remote client.
        using (Stream outStream = args.Socket.OutputStream.AsStreamForWrite())
        {
            using (StreamWriter writer = new StreamWriter(outStream))
            {
                await writer.WriteLineAsync(responseString);
                await writer.FlushAsync();
            }
        }
    }
    catch(Exception ex)
    {
        Debug.WriteLine("SocketListener failed." + ex.ToString());
    }
}

Client:

private async Task Start(int responseTimeout)
{
    CancellationTokenSource cts = new CancellationTokenSource();

    try
    {
        cts.CancelAfter(responseTimeout);

        //Create the StreamSocket and establish a connection to the echo server.
        socket = new StreamSocket();

        //The server hostname that we will be establishing a connection to. We will be running the server and client locally,
        //so we will use localhost as the hostname.
        HostName serverHost = new HostName(hostName);

        //Every protocol typically has a standard port number. For example HTTP is typically 80, FTP is 20 and 21, etc.
        //For the echo server/client application we will use a random port 1337.
        string serverPort = port.ToString();
        await socket.ConnectAsync(serverHost, serverPort).AsTask(cts.Token);
    }
    catch
    {
        socket.Dispose();
    }
}

public async Task<SocketResponse> SendCommand(SocketCommand command, int responseTimeout = 2000)
{
    // Connect to socket first
    await Start(responseTimeout);

    //Write data to the echo server.
    using (Stream streamOut = socket.OutputStream.AsStreamForWrite())
    {
        using (StreamWriter writer = new StreamWriter(streamOut))
        {
            // Serialize command to json and send
            string request = JsonHelper.ToJson<SocketCommand>(command);
            await writer.WriteLineAsync(request);
            await writer.FlushAsync();
        }
    }

    //Read data from the echo server.
    using (Stream streamIn = socket.InputStream.AsStreamForRead())
    {
        using (StreamReader reader = new StreamReader(streamIn))
        {
            string responseString = await reader.ReadLineAsync();
            return JsonHelper.FromJson<SocketResponse>(responseString);
        }
    }
}

I am encountering two issues with my code:

  1. I have to establish connection from client side before every request. What is the reason?
  2. When I am sending bigger response from server (string is 3kb) client waits for the response infinitely, however server responded quickly. Moreover, if server is closed, response is read by client. What's going wrong with this?
1

1 Answers

0
votes

If at all possible, I strongly, strongly recommend using SignalR instead of raw sockets. The MSDN socket examples in particular are absolutely not production quality. Writing proper raw socket code is extremely difficult.

To answer your specific questions:

I have to establish connection from client side before every request. What is the reason?

Because your code is closing the socket streams after every request.

When I am sending bigger response from server (string is 3kb) client waits for the response infinitely, however server responded quickly. Moreover, if server is closed, response is read by client. What's going wrong with this?

I don't see anything in the posted code that would cause this. In the general case, both the server and client should be reading from all open sockets all the time (even while writing) - otherwise you could get a kind of deadlock like this if the TCP windows fill up. But I don't think it would happen with an echo server and only 3K of data.

Again, I strongly recommend using SignalR; your code will end up much simpler. If you do need to use raw sockets, I have a TCP/IP sockets FAQ that may help.