# Python教程 - 13 网络编程
我们要在两个应用程序之间进行网络通信,需要使用套接字socket来实现。
socket由IP地址和端口号组成。IP地址用来确定是哪台设备,端口号用来确定是设备上的哪个程序。
在网络编程中,TCP和UDP是两个常用的协议,而socket是在应用层和传输层之间的接口,用于方便地使用TCP或UDP进行网络通信。
TCP和UDP协议的区别:
TCP是一种可靠的面向连接的协议,它在传输数据之前先建立一个连接,确保数据的可靠性和完整性,然后再进行数据传输。
UDP是一种不可靠的无连接协议,它直接将数据分组发送到目的地址,不需要建立连接,速度快,但数据传输的可靠性较差。
# 13.1 TCP通信
TCP通信是区分服务端和客户端的,服务器启动服务,指定IP和端口,等待客户端的连接。
客户端创建连接,指定服务端的IP和端口,连接到服务端,这样服务端和客户端就可以发送数据了。
# 1 编写服务端
1、创建socket套接字
import socket
# 1、创建TCP套接字
server_socket = socket.socket()
2
3
4
上面在创建socket的时候,没有明确指定参数,则使用的就是TCP的协议。也可以明确指定使用TCP’协议:
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
``socket.AF_INET表示使用 IPv4 地址族,socket.SOCK_STREAM` 表示使用 TCP 协议。
2、绑定套接字到本地地址和端口
server_socket.bind(('localhost', 8888))
端口可以自己选择,不要使用已经被使用的端口。
3、监听端口
server_socket.listen(5)
参数为允许连接的客户端数量,超出数量,客户端连接需要等待其他的客户端断开连接
4、等待客户端连接
client_socket, addr = server_socket.accept()
accept()方法是阻塞的,代码运行到这里会一直等待客户端的连接。accept()方法返回的是一个二元元组。
5、从客户端接收数据
ata = client_socket.recv(1024).decode('utf-8')
recv()方法时候读取客户端发送的内容,该方法是阻塞的,会一直等待客户端发送数据。参数指定的是缓冲区的大小。读取到数据后,通过 decode 方法对数据进行解码。
6、服务器发送数据给客户端
服务器也可以通过客户端的连接给客户端发送数据
client_socket.sendall('Hello, World!'.encode('utf-8'))
7、关闭客户端连接
client_socket.close()
8、关闭服务端socket
如果要退出程序,停止接收客户端的连接,则需要关闭服务器端的socket。
server_socket.close()
完整代码:
首先创建一个模块,例如tcp_server.py
import socket
# 1、创建TCP套接字
server_socket = socket.socket()
# 2、绑定套接字到本地地址和端口号
server_socket.bind(('localhost', 8888))
# 3、开始监听连接,参数为允许连接的客户端数量,超出数量,客户端连接需要等待其他的客户端断开连接
server_socket.listen(5)
# 4、等待客户端连接,accept方法是阻塞的,程序运行到这里,会一直等待客户端的连接
client_socket, addr = server_socket.accept()
print('客户端已连接:', addr)
while True:
    # 5、从客户端接收数据
    data = client_socket.recv(1024).decode('utf-8')
    print("接收到客户端的数据:", data)
    # 6、向客户端发送响应
    client_socket.sendall(f"响应:{data}".encode('utf-8'))
    
    if data == "exit":
        break
# 7、关闭客户端连接
client_socket.close()
# 8、关闭服务端socket
server_socket.close()
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
上面用了while循环,一直循环读取客户端发送的消息,当读取到exit时,退出。
# 2 编写客户端
1、创建socket套接字
import socket
# 1、创建TCP套接字
client_socket = socket.socket()
2
3
4
2、连接到服务器
client_sock.connect(('localhost', 8888))
指定要连接的服务端的IP和端口,需要和服务端监听的端口一致。
3、向服务端发送数据
client_sock.sendall("要发送的数据".encode('utf-8'))
4、接收服务端的数据
data = client_sock.recv(1024).decode("UTF-8")
5、关闭连接
如果要断开连接,则关闭连接。
client_sock.close()
完整代码:
再创建一个客户端模块,例如tcp_client.py
import socket
# 创建TCP套接字
client_sock = socket.socket()
# 连接到远程服务器
client_sock.connect(('localhost', 8888))
while True:
    # 这里从键盘接收数据
    msg = input("请输入:")
    # 向服务器发送数据
    client_sock.sendall(msg.encode('utf-8'))
    # 接收服务器响应
    data = client_sock.recv(1024).decode("UTF-8")
    # 处理响应数据
    print("服务端:", data)
    if msg == "exit":
        break
# 关闭连接
client_sock.close()
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
上面使用了一个 while 循环,主要是为了可以不停的通过键盘输入,向服务端发送数据。
当输入 exit 的时候,可以退出程序。
在运行的时候,我们先运行 tcp_server.py 模块,启动服务端。
然后再运行 tcp_client.py 模块,然后在客户端模块通过键盘输入内容发送到服务端,服务端会响应客户端的数据请求。
# 3 接收多个客户端连接
上面的服务端只能实收一个客户端的连接,因为只在一个线程中进行的。
如果要服务端接收多个客户端的连接,则需要使用多线程来实现。
import socket
import threading
# 处理客户端请求的函数
def handle_client(client_socket, addr):
    print('客户端已连接:', addr)
    try:
        while True:
            # 从客户端接收数据
            data = client_socket.recv(1024).decode('utf-8')
            print('收到数据:', data)
            # 向客户端发送响应
            client_socket.sendall(data.encode('utf-8'))
            if data == "exit":
                print("跳出循环")
                break
        print("关闭客户端连接")
        # 7、关闭客户端连接
        client_socket.close()
    except socket.error as e:
        print('客户端连接异常:', e)
    finally:
        # 关闭客户端连接
        client_socket.close()
        print('客户端已断开:', addr)
# 创建TCP套接字
sock = socket.socket()
# 绑定套接字到本地地址和端口号
sock.bind(('localhost', 8888))
# 开始监听传入连接
sock.listen(5)
# 循环处理客户端请求
while True:
    # 等待客户端连接
    client_socket, addr = sock.accept()
    # 启动一个新线程处理客户端请求
    client_thread = threading.Thread(target=handle_client, args=(client_socket, addr))
    client_thread.start()
# 8、关闭服务端socket
server_socket.close()
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
这样就可以接收多个客户端的连接了,每连接一个客户端会开启一个线程。
在运行客户端程序的时候,PyCharm默认一个模块是不能运行多个的,需要修改一下配置。
首先打开 Edit Configurations...

然后勾选 Allow parallel run 选项,这样客户端的 tcp_client.py 就可以运行多个了。

# 13.2 UDP通信
UDP通信比TCP通信更简单一些。使用UDP协议进行通信不需要建立连接,可以直接发送数据包。
# 1 编写服务端
创建UDP的 Socket 的时候,需要指定 UDP协议:
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
服务端是不需要等待客户端的连接的,直接接收客户端的数据即可。
完整代码:
import socket
# 1、创建UDP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2、绑定地址和端口
server_socket.bind(('localhost', 8888))
while True:
    # 3、接收数据
    data, client_address = server_socket.recvfrom(1024)
    msg = data.decode("utf-8")
    print("接收到数据:", msg, ", from", client_address)
    # 4、发送响应
    server_socket.sendto(msg.encode("utf-8"), client_address)
    if msg == "exit":
        break
# 5、关闭套接字
server_socket.close()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
发送数据需要使用 sendto 方法。
上面在接收到 exit 字符串后退出。
# 2 编写客户端
客户端也不需要连接服务端,只需要在发送数据的时候指定 IP 和端口。
import socket
# 1、创建UDP套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
    msg = input("请输入:")
    # 2、发送数据,需要指定地址和端口
    client_socket.sendto(msg.encode("utf-8"), ('localhost', 8888))
    # 3、接收响应
    data, server_address = client_socket.recvfrom(1024)
    print("响应:", data.decode("utf-8"), ", from", server_address)
    if msg == "exit":
        break
# 4、关闭套接字
client_socket.close()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
发送数据需要使用 sendto 方法。
上面在接收到 exit 字符串后退出。
← 12-多线程
