更好的阅读体验:点这里 ( www.foooor.com
)
12 网络编程
我们要在两个应用程序之间进行网络通信,需要使用套接字socket来实现。
socket由IP地址和端口号组成。IP地址用来确定是哪台设备,端口号用来确定是设备上的哪个程序。
在网络编程中,TCP和UDP是两个常用的协议,而socket是在应用层和传输层之间的接口,用于方便地使用TCP或UDP进行网络通信。
TCP和UDP协议的区别:
TCP是一种可靠的面向连接的协议,它在传输数据之前先建立一个连接,确保数据的可靠性和完整性,然后再进行数据传输。
UDP是一种不可靠的无连接协议,它直接将数据分组发送到目的地址,不需要建立连接,速度快,但数据传输的可靠性较差。
12.1 TCP通信
TCP通信是区分服务端和客户端的,服务器启动服务,指定IP和端口,等待客户端的连接。
客户端创建连接,指定服务端的IP和端口,连接到服务端,这样服务端和客户端就可以发送数据了。
1 编写服务端
1、创建socket套接字
import socket
# 1、创建TCP套接字
server_socket = socket.socket()
上面在创建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()
上面用了while循环,一直循环读取客户端发送的消息,当读取到exit时,退出。
2 编写客户端
1、创建socket套接字
import socket
# 1、创建TCP套接字
client_socket = socket.socket()
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()
上面使用了一个 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()
这样就可以接收多个客户端的连接了,每连接一个客户端会开启一个线程。
在运行客户端程序的时候,PyCharm默认一个模块是不能运行多个的,需要修改一下配置。
首先打开 Edit Configurations...
然后勾选 Allow parallel run
选项,这样客户端的 tcp_client.py
就可以运行多个了。
12.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()
发送数据需要使用 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()
发送数据需要使用 sendto
方法。
上面在接收到 exit
字符串后退出。