在X交互之后,使用TcpClient(套接字或流)通过网络发送和接收失败

时间:2014-02-08 02:39:28

标签: c# sockets tcp client-server tcpclient

我有几个非常简单的套接字程序来做长度前缀的字符串发送和接收。这是核心例程:

// 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个字节       响应:       foob​​ar的

> 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次。但它会挂起。

有什么想法吗?

2 个答案:

答案 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)

我能够验证客户端和服务器 - 代码被剥离回上面发布的更简单的版本 - 当东海岸客户端点击西海岸服务器时,对于大量的交互完全正常。必须是我本地网络中的东西。不是软件问题。感谢大家的见解。