网络交互离不开socket,浏览器访问数据是请求-应答模式,服务器收到数据后会根据请求地址解析并返回对应文件内容(io)。本文以tcp为例,使用python3模拟服务端,使用浏览器作为客户端,来实现一个线程为多个浏览器客户端服务的示例。

先来看下通常的服务端启动程序,服务端根据host和port创建套接字,注意这里要调用listen方法开始监听,然后才能accept接收客户端连接。

(hostport):
    server = socket.socket(socket.AF_INETsocket.SOCK_STREAM)
    server.bind((hostport))
    server.listen()
    server

传统的做法是服务端socket在无限循环中accept,然而accept方法默认是阻塞的,会阻塞在那里直到有客户端来连接,所以单线程时无法为多个客户端同时服务。

:
    clientaddr = server.accept()

如果能解阻塞,那么线程自然可以继续向下执行提供服务,我们可以将服务端socket设置为非阻塞。注意这里要捕获异常,防止没有客户端连接到来导致程序异常退出。

server.setblocking(False)
:
    :
        clientaddr = server.accept()
    :
        :
        (% addr)

以上我们就解决了accept阻塞的问题,接下来就可以根据客户端socket进行相应的读写处理了。使用recv方法进行数据读取,这里简单设置为每次读取1024字节。

data = client.recv(1024)

因为客户端socket默认也是阻塞的,所以recv方法会阻塞等待数据到来,所以想要线程继续向下执行,我们要解决recv阻塞问题。同服务端socket设置,将客户端socket设置为非阻塞。

server.setblocking(False)
:
    :
        clientaddr = server.accept()
    :
        :
        (% addr)
        client.setblocking()
    :
        data = client.recv()
    :
        :
        data:
            (% ((addr)data.decode()))
        :
            client.close()

以上我们就实现了客户端非阻塞,这样客户端socket接收数据也变成非阻塞的了,注意同样需要捕获异常,防止程序异常退出。现在可以接收多个客户端请求了,但是无法为客户端服务,因为服务端只接受了客户端数据,还没有做出响应。先来实现一个响应客户端的方法。

(client):
    response = response += response += response += % random.randint()
    client.send(response.encode())
    client.close()

响应有了,依然要考虑新的问题,服务端也不知道服务哪个client,所以我们需要找一个地方把连接进来的客户端socket保存起来然后循环迭代处理。最终整理如下,把阻塞的地方消灭掉,使得线程可以继续向下执行,完成一个进程下一个线程为多个客户端服务。

random
socket


(hostport):
    server.setblocking()
    server

clients = ()

():
    server = bind()
    :
        :
            clientaddr = server.accept()
        :
            :
            (% addr)
            client.setblocking()
            clients.append(client)
        cli clients:
            :
                data = cli.recv()
            :
                :
                data:
                    (% ((addr)data.decode()))
                    send(cli)
                :
                    clients.remove(cli)
                    cli.close()


__name__ == :
    main()

下面我们使用浏览器访问一下,http://127.0.0.1:8000,效果如下:

【py3】使用单线程服务多个客户端请求_java【py3】使用单线程服务多个客户端请求_java_02