【问题标题】:Why can't I use the TCP client to send two message? (continuation)为什么我不能使用 TCP 客户端发送两条消息? (续)
【发布时间】:2016-04-25 00:43:48
【问题描述】:

这是an earlier question 的延续。我正在启动一个新线程,以便展示最新的完整服务器和客户端代码,包括我在该线程和 Reddit 上收到的所有建议。

我在 Windows 7 Pro 下运行。

我有一个用 Python 编写的非常简单的 TCP 服务器。它创建一个 socketserver 对象并等待消息。当一个到达时,服务器将它打印到它的控制台并通过相同的端口发送一个简单的确认。

客户端向服务器发送一个编号的消息,等待确认,然后显示它。然后它询问用户是否应该发送另一条消息。

第一条消息已成功发送并确认。在客户端,似乎第二条消息发送成功;对网络流的 Write() 方法的调用成功。但是当调用 Read() 消息来获得确认时,会抛出异常:“已建立的连接已被主机中的软件中止。”

这是服务器代码:

import json
import threading

import socketserver
import time

with open('CAPS_TWMS_config.json', 'rt') as c:
    caps_config = json.load(c)

# We are listening on this port and all defined IP addresses
# listenPort = 5001
listenPort = caps_config["listen_port"]

# Were to send the information to.
clientIPAddress = '127.0.0.1'   # socket.gethostbyname('client')
# clientPort = 12345
clientPort = caps_config["send_port"]

dsnName = caps_config["dsn_name"]

# Message sequence number
sequence_num = 1
exit_app = False

class ListenSocketHandler(socketserver.BaseRequestHandler):
    """
    The RequestHandler class for our server.

    It is instantiated once per connection to the server, and must
    override the handle() method to implement communication to the
    client.
    """
    def __init__(self, request, client_address, this_server):
        socketserver.BaseRequestHandler.__init__(self, request, client_address, this_server)
        self.timeout = 10

    def handle(self):
        try:
            data = self.request.recv(1024).decode()
            # print (str.format("dataString[21]: {0}; dataString[24:26]: {1}", data[21], data[24:26]))
            print ('ListenSocketHandler recv()-> "%s"' % data)
            print ('ListenSocketHandler recv().length-> "%d"' % len(data))

            if len(data) > 0:
                self.request.send("I got a message!".encode())
                return
        except Exception as value:
            print('ListenSocketHandler - %s' % str(value))

        return


class ListenServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    """
    The multi-threaded server that will spawn threads running the Socket Handler for each
    connection
    """
    pass

if __name__ == '__main__':

    try:
        # Create the Server Handler for connections to this computer listening on all IP addresses,
        # change '' to 'x.x.x.x'  to listen on a specific IP network.  This class will listen for messages    # from CAPS.
        server = ListenServer(('', listenPort), ListenSocketHandler)
        ip, port = server.server_address

        # Start a thread with the server -- that thread will then start one
        # more thread for each request
        server_thread = threading.Thread(target=server.serve_forever)
        # Exit the server thread when the main thread terminates
        server_thread.setDaemon(True)
        server_thread.start()

        while not exit_app:
            time.sleep(1)

        print ('Out of main loop.')
        server.shutdown()
        server.server_close()
    except Exception as value:
        print("Failed to do something: %s", str(value))

这是客户端代码:

private void button1_Click(object sender, EventArgs e)
{
    TcpClient client = new TcpClient();

    try
    {
        client.Connect("127.0.0.1", 5001);
        NetworkStream stream = client.GetStream();
        int messageCount = 1;

        while (true)
        {                    
            // Translate the passed message into ASCII and store it as a Byte array.
            string message = string.Format("This is message {0}", messageCount++);
            Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);


            // Send the message to the connected TcpServer. 
            stream.Write(data, 0, data.Length);

            // Receive the TcpServer.response.

            // Buffer to store the response bytes.
            data = new Byte[1024];

            // String to store the response ASCII representation.
            String responseData = String.Empty;

            // Read the first batch of the TcpServer response bytes.
            Int32 receivedCount = 0;
            int sleepCount = 0;
            while (receivedCount == 0)
            {                        
                receivedCount = stream.Read(data, 0, data.Length);
                responseData = System.Text.Encoding.ASCII.GetString(data, 0, receivedCount);
                if (receivedCount == 0)
                {
                    Thread.Sleep(250);
                    if (sleepCount++ > 20)
                    {
                        MessageBox.Show("Response timeout.");
                        break;
                    }
                }
            }

            if (MessageBox.Show("Reply: " + responseData + "  Try again?", "Try again?", MessageBoxButtons.YesNo) == DialogResult.No)
            {
                break;
            }

        }
        client.Close();
    }
    catch (Exception ex)
    {
        MessageBox.Show("Failed to do something with TcpClient: " + ex.Message);
    }
}

【问题讨论】:

  • 你希望服务器如何知道一个请求的结束和下一个请求的开始?
  • 另外,在从处理程序返回之前,请尝试self.request.shutdown(socket.SHUT_RDWR)
  • 检查您是否正在访问:client.Close();。 TCP 在消息中获取零字节的数据是正常的,因此接收计数可能为零,您将退出 while 循环。您需要接收,直到收到所有数据。因此使用了 3 种方法中的 1 种 1) Ascii :接收直到收到已知字符,如 '\n' 2) Ascii 或 Binary :将字节数添加到消息的开头并读取,直到收到所有字节。 3) Ascii 或 Binary:固定长度消息。每条消息(或消息类型)都有已知的字符数。
  • 此时,我不关心识别一个请求的结束和下一个请求的开始。一旦我弄清楚如何让连接的两端保持通话,我会担心的。
  • 我下载了 TCPView。当我启动 Python 服务器时,我看到在 LISTENING 状态下创建的端口。当我启动客户端时,端口的状态变为 ESTABLISHED。在收到并确认第一条消息后,套接字的状态更改为 FIN_WAIT2,并且计数器显示预期的数字。当发送第二条消息时,TCPView 显示中我的端口的行会立即闪烁红色。如果我不闪烁,有时我会看到收到的数据包计数变为 2。当红色清除时,端口已恢复为 LISTENING 并且计数器已清除。

标签: c# python sockets tcp


【解决方案1】:

在您的代码中,每次连接新客户端时,它都会读取数据并向客户端确认,并且处理程序在以下语句之后立即关闭(即返回)

 self.request.send("I got a message!".encode())
                return

因此连接已关闭。来自客户端的进一步命令未发送。您可以循环等待接收和确认。

【讨论】:

  • 我不认为这是正确的。在创建 socketserver 对象时,指定 ListenSocketHandler 类来处理传入数据。当收到消息时,会调用 ListenSocketHandler 类的 handle() 方法。该方法处理消息(通过发送确认)并返回。该方法并非设计为永远在循环中运行,并且从该方法返回不会终止连接。
  • 哦..是这样吗?我的理解是默认连接处理程序(ListenSocketHandler)不会做任何事情,除非你在 handle() 方法中实现一些东西。因此,对于从客户端连接的每个新连接,而不是来自客户端的每条消息,都会调用您的 handle()。这是简单的 echo tcp 服务器 [链接] (docstore.mik.ua/orelly/other/python/…)。
  • 您是否通过添加循环检查过?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多