多线程

需要添加模块 import threading

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import threading
#提供一个线程的起始位置(线程回调函数)
def work():
for i in range(1000):
#循环输出一千次线程名称(创建线程时指定)
#在python中线程由pvm维护,是伪线程
print(i, threading.current_thread().name)

#若当前模块是主模块就执行
if __name__ == "__main__":
#创建一个线程对象,指定起始位置和名称
t = threading.Thread(target = work, name = "Work")
#执行线程
t.start()
#输出主线程的名称
for i in range(1000):
print(i, threading.current_thread().name)
#主线程等待线程执行完毕
t.join()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import threading
import time
#使用线程模块中提供的锁Lock
lock = threading.Lock()

#提供一个线程的起始位置(线程回调函数)
def work():
for i in range(1000):
#将需要保护的代码添加到acquire和release之间
time.sleep(1)
lock.acquire()
print(i, threading.current_thread().name)
lock.release()

if __name__ == "__main__":
#创建线程并执行
threading.Thread(target = work, name = "Work").start()
#输出主线程的名称,应该和线程的输出是交替的
for i in range(1000):
time.sleep(1)
lock.acquire()
print(i, threading.current_thread().name)
lock.release()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import threading
#变参函数接收的数据被打包成元组
def work_thread1(*args):
print(type(args), args)

# 一个变参函数,接收的数据被打包成字典
def work_thread2(**kwargs):
print(type(kwargs), kwargs)

# 可以传入元组和键值对
def work_thread3(*args, **kwargs):
print(type(args), args)
print(type(kwargs), kwargs)

#创建线程,传入多个参数
threading.Thread(target = work_thread1, args = (1, 2, 3)).start()
threading.Thread(target = work_thread2, kwargs = {"a":1, "b":2}).start()
threading.Thread(target = work_thread3, args=(1, 2, 3), kwargs={"a":1, "b": 2}).start()

在 Python 中函数的递归有一定的次数限制,大概是1000,Python 执行在 pvm 中,效率不高,故不建议使用递归

通信

  1. 不能连续发送消息

    服务端:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #导入套接字模块socket
    import socket

    def main():
    #创建一个用于网络连接的套接字,使用TCP协议
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    #绑定套接字到执行的ip和端口
    server.bind(("127.0.0.1", 0x1515))
    #设置套接字为监听状态,同一时刻能接收的数量
    server.listen(socket.SOMAXCONN)
    #等待客户端的连接
    client, address = server.accept()
    #和客户端使用send recv进行交互
    while True:
    s = input(">> ")
    #发送的数据必须是bytes类型的,需要编码
    client.send(s.encode())
    #等待客户端的回复,接收的数据是字节,需要解码
    print(client.recv(100).decode())

    if __name__ == "__main__":
    main()

    客户端:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #导入套接字
    import socket

    def main():
    #创建一个用于网络连接的套接字,使用 TCP 协议
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    #等待客户端的连接
    client.connect(("127.0.0.1", 0x1515))
    #和服务端使用 send recv 进行交互
    while True:
    #等待服务器的回复,接收的数据是字节,需要解码
    print(client.recv(100).decode())
    s = input(">> ")
    #发送的数据必须是 bytes 类型的,需要编码
    client.send(s.encode())

    if __name__ == "__main__":
    main()
  2. 通过线程接收消息,不需要等待

    服务端:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    #导入套接字模块socket
    import socket
    import threading

    #接收数据的线程
    def reciver_thread(client):
    while True:
    #不断打印接收到的数据
    print(client.recv(100).decode())

    def main():
    #创建一个用于网络连接的套接字,使用TCP协议
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    #绑定套接字到执行的ip和端口
    server.bind(("127.0.0.1", 0x1515))
    #设置套接字为监听状态,同一时刻能接收的数量
    server.listen(socket.SOMAXCONN)
    #等待客户端的连接
    client, address = server.accept()
    threading.Thread(target = reciver_thread, args = (client,)).start()
    #和客户端使用send recv进行交互
    while True:
    s = input(">> ")
    #发送的数据必须是bytes类型的,需要编码
    client.send(s.encode())

    if __name__ == "__main__":
    main()

    客户端:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    #设置当前文件的编码方式,编码方式和解码方式要保持一致
    #encoding: UTF-8

    #导入套接字
    import socket
    import threading

    #接收数据的线程
    def reciver_thread(client):
    while True:
    #不断打印接收到的数据
    print(client.recv(100).decode())

    def main():
    #创建一个用于网络连接的套接字,使用 TCP 协议
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    #等待客户端的连接
    client.connect(("127.0.0.1", 0x1515))
    threading.Thread(target = reciver_thread, args = (client,)).start()
    #和服务端使用 send recv 进行交互
    while True:
    s = input(">> ")
    #发送的数据必须是 bytes 类型的,需要编码
    client.send(s.encode())

    if __name__ == "__main__":
    main()
  3. 服务端发挥其作用,作为中转站转发消息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    #导入套接字模块socket
    import socket
    import threading

    #维护一个列表,保存所有在线的客户端
    clients = []

    #接收数据的线程函数
    def reciver_thread(client):
    while True:
    try:
    #这里接收到某一客户端的消息,需要转发
    msg = client.recv(100)
    #遍历客户端,若非自己,就转发
    for i in clients:
    if i != client:
    i.send(msg)
    except Exception as e:
    #直接在列表中删除当前的客户端
    for i in clients:
    if i == client:
    clients.remove(i)
    print(client.getpeername(), "下线了")
    break
    #不断打印接收到的数据
    print(client.recv(100).decode())

    def main():
    #创建一个用于网络连接的套接字,使用TCP协议
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    #绑定套接字到执行的ip和端口
    server.bind(("127.0.0.1", 0x1515))
    #设置套接字为监听状态,同一时刻能接收的数量
    server.listen(socket.SOMAXCONN)
    #循环等待客户端的连接
    while True:
    #连接到客户端,输出其地址
    client, address = server.accept()
    #将连接到的客户端加到在线列表中
    clients.append(client)
    print(address, "上线了")
    #每个客户端都应有一个独立的接收数据的线程
    threading.Thread(target = reciver_thread, args = (client,)).start()

    if __name__ == "__main__":
    main()
  4. 编码

    服务端:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #encoding: utf8

    # 导入套接字模块 socket
    import socket

    # main 函数,为主模块时执行
    def main():
    # 1. 创建一个用于网络连接的套接字,使用 TCP 协议
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 2. 绑定套接字到执行的 ip 和 端口,必须是元组
    server.bind(("127.0.0.1", 6666))

    # 3. 设置套接字为监听状态,同一时刻能接收的数量
    server.listen(socket.SOMAXCONN)

    # 4. 等待客户端的连接
    client, address = server.accept()

    # Python 向 C++ 发送一个 char*(GBK) 类型的字符串
    client.send("你好".encode("gbk"))
    print(client.recv(100).decode("gbk"))

    # Python 向 C++ 发送一个 wchar_t*(UTF16LE) 类型的字符串
    client.send("你好".encode("utf-16LE"))
    print(client.recv(100).decode("utf-16LE"))

    if __name__ == "__main__":
    main()

    C++ 客户端:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    // .h文件
    #pragma once
    #include <iostream>
    //包含windows网络编程必须的头文件
    #include<WinSock2.h>
    #pragma comment(lib, "ws2_32.lib")
    #include <windows.h>
    #include <ws2tcpip.h>
    //传入一个表达式,并判断结果
    void CheckResult(BOOL result, const char* str)
    {
    //如果表达式的结果为假,就报错并且结束程序
    if (!result)
    {
    printf("%s\n", str);
    system("pause"); exit(0);
    }
    }
    //连接到服务器
    SOCKET connect_server()
    {
    // 1. 初始化网络套接字模块
    WSAData WsaData = { 0 };
    if (!WSAStartup(0x0202, &WsaData))
    CheckResult(0x0202 == WsaData.wVersion, "指定版本请求失败");

    // 2. 创建一个套接字[IP:PORT]
    SOCKET ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    CheckResult(ClientSocket != INVALID_SOCKET, "套接字创建失败");

    // 4. 指定服务器的IP和地址
    sockaddr_in ServerAddr = { 0 };
    ServerAddr.sin_family = AF_INET;
    ServerAddr.sin_port = htons(6666);
    inet_pton(AF_INET, "127.0.0.1", &ServerAddr.sin_addr.S_un);

    // 5. 连接到目标服务器
    DWORD result = connect(ClientSocket, (sockaddr*)& ServerAddr, sizeof(ServerAddr));
    CheckResult(result != SOCKET_ERROR, "连接服务器失败");

    // 6. 返回套接字进行数据的收发
    return ClientSocket;
    }


    // .cpp文件
    #include "header.h"
    #include <iostream>
    #include <AtlBase.h>
    using namespace std;
    int main()
    {
    // 连接到服务器
    SOCKET client_socket = connect_server();

    // C++ 中 char 的默认编码是 GBK
    char buffer[0x100] = { 0 };
    recv(client_socket, buffer, 0x100, 0);
    puts(buffer);
    send(client_socket, "你好", 4, 0);

    // C++ 中 wchar_t 的默认编码是 UTF-16LE
    wchar_t buf[0x100] = { 0 };
    recv(client_socket, (char*)buf, 0x100, 0);
    // 将宽字符转换成多字节
    printf("%s\n", CW2A(buf).m_szBuffer);
    cin >> buffer;
    CA2W wbuffer(buffer);
    send(client_socket, (char*)wbuffer.m_szBuffer,
    wcslen(wbuffer.m_szBuffer) * 2, 0);
    system("pause");
    return 0;
    }