文章目录
套接字编程实战
socket对象的内置函数和属性
UDP服务器最简化案例
from socket import *
# 最简化的UDP服务器代码
# AF_INET指定使用IPv4地址
# SOCK_DGRAM指定使用UDP协议
s = socket(AF_INET, SOCK_DGRAM) # 创建UDP类型的套接字
# 这里绑定的是本地回环地址的8888端口,表示服务器将在本地的8888端口上监听
# 如果IP地址留空或设置为None,服务器将监听所有可用的网络接口上的指定端口
s.bind(("127.0.0.1", 8888)) # 绑定端口,ip可以不写
print("等待接受数据!")
# recv()方法用于接收数据,它将阻塞直到有数据到达
# 返回值是一个包含两部分的元组:数据内容和发送方的地址信息
recv_data = s.recv(1024) # 1024代表本次接受的最大字节数
print(f"收到远程信息:{recv_data[0].decode('gbk')}, from {recv_data[1]}")
# 关闭套接字
s.close()
这段代码的逻辑如下:
- 导入socket库。
- 创建一个UDP套接字。
- 将套接字绑定到本地的8888端口上,准备接收数据。
- 打印一条消息,通知服务器正在等待接收数据。
- 使用
recv
方法接收数据。这里设置的1024是缓冲区的大小,即服务器一次能够接收的最大数据量。 - 打印出接收到的数据以及发送方的地址信息。
- 最后关闭套接字,释放资源。
UDP客户端最简化案例
from socket import *
# 创建UDP类型的套接字
# AF_INET指定使用IPv4地址
# SOCK_DGRAM指定使用UDP协议
s = socket(AF_INET, SOCK_DGRAM)
# 定义服务器的地址和端口,这里使用的是本地回环地址和端口号8888
address = ("127.0.0.1", 8888)
# 提示用户输入数据,input函数会读取用户输入的字符串
data = input("请输入: ")
# sendto方法用于发送数据到指定的地址
# data.encode("gbk")将字符串数据转换为GBK编码的字节流,因为socket发送的是字节数据
# 第二个参数address是元组,包含服务器的IP地址和端口号
s.sendto(data.encode("gbk"), address)
# 关闭套接字,释放系统资源
s.close()
这段代码的逻辑是:
- 导入socket库。
- 创建一个UDP套接字。
- 定义服务器的IP地址和端口号。
- 提示用户输入要发送的数据。
- 使用
sendto
方法将用户输入的数据发送到服务器。这里,data.encode("gbk")
是将输入的字符串转换为字节流,因为UDP协议发送的是字节数据。 - 关闭套接字。
UDP服务端持续接收信息
from socket import *
s = socket(AF_INET, SOCK_DGRAM) # 创建UDP类型的套接字
s.bind(("127.0.0.1", 8888)) # 绑定端口,ip可以不写
print("等待接受数据!")
while True:
recv_data = s.recvfrom(1024) # 1024 代表本次接受的最大字节数
recv_content = recv_data[0].decode("gbk")
print(f"收到远程消息: {recv_content}, from {recv_data[1]}")
if recv_content == "88":
print("结束聊天!")
break
# 关闭套接字
s.close()
UDP客户端持续发送信息
from socket import *
s = socket(AF_INET, SOCK_DGRAM)
address = ("127.0.0.1", 8888)
while True:
data = input("请输入: ")
s.sendto(data.encode("gbk"), address)
if data == "88":
print("结束聊天!")
break
# 关闭套接字,释放系统资源
s.close()
UDP和多线程实现双向自由通信
客户端
from socket import *
from threading import Thread
def receive_data():
while True:
receive_data = s.recvfrom(1024)
receive_content = receive_data[0].decode("gbk")
print(f"收到远程信息: {receive_content}, from {receive_data[1]}")
if receive_content == "88":
print("结束聊天")
break
def send_data():
address = ("127.0.0.1", 8888)
while True:
data = input("请输入: ")
s.sendto(data.encode("gbk"), address)
if data == "88":
print("结束聊天")
break
if __name__ == '__main__':
s = socket(AF_INET, SOCK_DGRAM)
s.bind(("127.0.0.1", 9999))
t1 = Thread(target=receive_data)
t2 = Thread(target=send_data)
t1.start()
t2.start()
t1.join()
t2.join()
服务端
from socket import *
from threading import Thread
def receive_data():
while True:
receive_data = s.recvfrom(1024) # 表示本次接受的最大字节数为1024
receive_content = receive_data[0].decode("gbk")
print(f"收到远程消息:{receive_content}, from {receive_data[1]}")
if receive_content == "88":
print("结束聊天")
break
def send_data():
address = ("127.0.0.1", 9999)
while True:
data = input("请输入: ")
s.sendto(data.encode("gbk"), address)
if data == "88":
print("结束聊天")
break
if __name__ == '__main__':
s = socket(AF_INET, SOCK_DGRAM) # 创建UDP类型的套接字
# 示服务器将在本地的8888端口上监听
s.bind(('127.0.0.1', 8888)) # 绑定端口,ip可以不写
t1 = Thread(target=receive_data)
t2 = Thread(target=send_data)
t1.start()
t2.start()
t1.join()
t2.join()
TCP服务端简化案例
from socket import *
# 建立TCP类型的套接字
server_socket = socket(AF_INET, SOCK_STREAM)
# 本机监听8899端口
# bind方法将套接字绑定到特定的地址和端口上,以便开始监听
server_socket.bind(("127.0.0.1", 8899))
# 开始监听传入连接,最多可以有5个客户连接同时等待接入
server_socket.listen(5)
print("等待接受连接!")
# accept方法用于接受一个新连接,它将阻塞直到一个连接到达
# 返回值是一个元组,包含两个元素:客户端套接字和客户端地址信息
client_socket, client_info = server_socket.accept()
# 使用recv方法从客户端套接字接收数据,缓冲区大小为1024字节
# recv方法将阻塞直到有数据到达
receive_data = client_socket.recv(1024)
# 将接收到的字节数据解码为GBK格式的字符串,并打印出来
# 同时打印出客户端的地址信息
print(f"收到信息: {receive_data.decode('gbk')}, 来自: {client_info}")
# 关闭与客户端的连接套接字
client_socket.close()
# 关闭监听套接字,释放资源
server_socket.close()
这段代码逻辑:
- 导入socket库。
- 创建一个TCP类型的套接字。
- 将套接字绑定到本机的8899端口上。
- 调用
listen
方法开始监听传入连接,最多可以有5个连接同时等待接入。 - 打印一条消息,通知服务器正在等待接受连接。
- 使用
accept
方法接受一个新连接。accept
方法将阻塞直到一个连接到达,返回一个包含客户端套接字和客户端地址信息的元组。 - 使用
recv
方法从客户端套接字接收数据,缓冲区大小为1024字节。 - 将接收到的字节数据解码为GBK格式的字符串,并打印出来。同时打印出客户端的地址信息。
- 关闭与客户端的连接套接字。
- 关闭监听套接字。
TCP客户端简化案例
from socket import *
client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect(('127.0.0.1', 8899))
client_socket.send("hello".encode("gbk"))
client_socket.close()
TCP服务端持续交互
from socket import *
server_socket = socket(AF_INET, SOCK_STREAM) # 建立TCP套接字
server_socket.bind(("127.0.0.1", 8899)) # 本机监听8899端口
server_socket.listen(5)
print("等待接受连接!")
client_socket, client_info = server_socket.accept()
print("一个客户端建立连接成功!")
while True:
receive_data = client_socket.recv(1024)
receive_content = receive_data.decode("gbk")
print(f"客户端说: {receive_content}, 来自: {client_info}")
if receive_content == "end":
break
msg = input(">")
client_socket.send(msg.encode("gbk"))
client_socket.close()
server_socket.close()
TCP客户端持续交互
from socket import *
client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect(("127.0.0.1", 8899))
while True:
# 给服务器发消息
msg = input(">")
client_socket.send(msg.encode("gbk"))
if msg == "end":
break
# 接受服务器端数据
receive_data = client_socket.recv(1024)
print(f"服务器端说: {receive_data.decode('gbk')}")
client_socket.close()
TCP服务端增加多线程
from socket import *
from threading import Thread
def receive_data():
while True:
receive_data = client_socket.recv(1024)
receive_content = receive_data.decode("gbk")
print(f"客户端说: {receive_content}, 来自: {client_info}")
if receive_content == "end":
print("结束连接!")
break
def send_data():
while True:
msg = input(">")
client_socket.send(msg.encode("gbk"))
if msg == "end":
print("结束连接!")
break
if __name__ == '__main__':
server_socket = socket(AF_INET, SOCK_STREAM) # 建立TCP套接字
server_socket.bind(("127.0.0.1", 8899)) # 本机监听8899端口
server_socket.listen(5)
print("等待接受连接!")
client_socket, client_info = server_socket.accept()
print("一个客户端建立连接成功!")
t1 = Thread(target=receive_data)
t2 = Thread(target=send_data)
t1.start()
t2.start()
t1.join()
t2.join()
client_socket.close()
server_socket.close()
TCP客户端增加线程
from socket import *
from threading import Thread
def receive_data():
while True:
# 接受服务器端数据
receive_data = client_socket.recv(1024)
receive_content = receive_data.decode('gbk')
print(f"服务器端说: {receive_content}")
if receive_content == "end":
print("结束连接")
break
def send_data():
while True:
# 给服务器发消息
msg = input(">")
client_socket.send(msg.encode("gbk"))
if msg == "end":
break
if __name__ == '__main__':
client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect(("127.0.0.1", 8899))
t1 = Thread(target=receive_data)
t2 = Thread(target=send_data)
t1.start()
t2.start()
t1.join()
t2.join()
client_socket.close()
碰到的问题
OSError: [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。
- 解决方法:通常是客户端和服务端的接口设置的不对,参考上面的代码
UDP和多线程实现双向自由通信