预计所需阅读时间:5分钟

昨天定了篇关于《Windows系统下Python编码的问题与解决方法》,讲的是编码的问题。今天要讲Python在Windows系统下第二问题,多线程问题。面目前来看,其实Windows下Python编程多出来的问题,主要这两个:编码与多线程。

在使用用Socket来编程时,会出现OSError: [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次,这样的提示

import socket
import multiprocessing

def send(new):
    data = new.recv(1024)
    print(data.decode("gbk"))

def main():
    tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp.bind(("", 7890))
    tcp.listen()
    print("*"*30)
    while True:
        new, address = tcp.accept()
        print("连接成功")
        p = multiprocessing.Process(target=send, args=(new, ))
        p.start()
        new.close()
    tcp.close()

main()

原因是在Windows中因为子进程会复制主进程中所有代码,导致相同的端口在子进程中同样被绑定同样的端口,解决办法是程序最后做一个判断:

if __name__ == '__main__':
    main()

因此,也不能用os.fork()来创创建子进程。而在Linux系统下可以fork,新建的子进程与父进程使用不同的进程号PID。

还有在使用进程池的时候,也一定要加这上这句判断,否则子进程会递归的添加进程到进程池形成死循环。

如果使用多线程池来写Socket TCP服务可以改写成这种形成:

# coding:utf-8
import os
from socket import socket, AF_INET, SOCK_STREAM
from multiprocessing import Pool
from threading import Thread


def server(client):
    print("server_pid=", os.getpid())

    def chat(client):
        while True:
            try:
                message = client.recv(1024)
            except:
                break
            else:
                print(message.decode("utf-8"))
                # 空字符串跳过
                if not message:
                    continue
                if message == "quit":
                    print("close")
                    client.close()
                    break
                else:
                    print("send")
                    ack_message = "\r\n %s received..." % str(os.getpid())
                    client.send(bytes(ack_message, encoding="utf-8"))

    t = Thread(target=chat, args=(client,))
    t.start()


if __name__ == "__main__":
    s_server = socket(AF_INET, SOCK_STREAM)
    s_server.bind(("127.0.0.1", 8000))
    s_server.listen()
    p = Pool(3)
    while True:
        client, addr = s_server.accept()
        print(client)
        p.apply(server, args=(client,))

在服务器端中使用多线程池,来连接不同的客户端,里面的server函数也可以只写成一个循环的形式,不用多线程。