客户端/服务器与StreamSockets通信 - 客户端无限期等待

时间:2016-01-26 10:13:23

标签: c# sockets client-server

根据MSDN我有以下代码来创建与TCP / IP StreamSockets的客户端/服务器通信。

服务器:

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());
    }
}

客户端:

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);
        }
    }
}

我的代码遇到两个问题:

  1. 我必须在每次请求之前从客户端建立连接。是什么原因?
  2. 当我从服务器发送更大的响应时(字符串是3kb),客户端无限地等待响应,但服务器响应很快。此外,如果服务器关闭,客户端将读取响应。这有什么问题?

1 个答案:

答案 0 :(得分:0)

如果可能的话,我强烈地, 强烈 建议使用SignalR而不是原始套接字。特别是MSDN套接字示例绝对不是生产质量。编写正确的原始套接字代码非常困难。

回答您的具体问题:

  

我必须在每次请求之前从客户端建立连接。是什么原因?

因为您的代码在每次请求后关闭套接字流。

  

当我从服务器发送更大的响应时(字符串为3kb),客户端无限地等待响应,但是服务器响应很快。此外,如果服务器关闭,客户端将读取响应。这有什么问题?

我发布的代码中没有任何内容会导致这种情况发生。在一般情况下,服务器和客户端应该始终从所有打开的套接字读取(即使在写入时) - 否则如果TCP窗口填满,您可能会遇到类似这样的死锁。但我不认为使用echo服务器只会发生3K数据。

同样,我强烈建议使用SignalR;您的代码最终将更多更简单。如果您需要使用原始套接字,我可以使用TCP/IP sockets FAQ