准备阶段:
需要导入socket库和threading库,如没有请自行安装,本次任务是在python3.9的编译器下运行(版本可自己选择,并不需要相同)
关于socket
socket 的典型应用就是 Web 服务器和浏览器:浏览器获取用户输入的URL,向服务器发起请求,服务器分析接收到的URL,将对应的网页内容返回给浏览器,浏览器再经过解析和渲染,就将文字、图片、视频等元素呈现给用户。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。
socket也分为阻塞式和非阻塞式:
- 阻塞式的socket的recv服从这样的规则:当缓冲区内有数据时,立即返回所有的数据;当缓冲区内无数据时,阻塞直到缓冲区中有数据。
- 非阻塞式的socket的recv服从的规则是:当缓冲区内有数据时,立即返回所有的数据;当缓冲区内无数据时,产生EAGAIN的错误并返回(在Python中会抛出一个异常)。
正常情况下,socket默认的是阻塞式的,接下来我们看看服务端和客户端的构建。
服务端与客户端
服务端创建可供连接的IP地址,accept会一直阻塞,直到有客户端连接才会处理客户端的请求,服务端与客户端的通信流程如下图所示:
项目代码
服务端:
import socket
import sys
import threading
def server_bind(socket_dict):
sock1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建 Socket 连接(TCP)
print('Socket Created')
try:
sock1.bind(('127.0.0.1', 8001)) # 配置 Socket,绑定 IP 地址和端口号 127.0.0.1是本地ip
except socket.error as e:
print('Bind Failed...', e)
sys.exit(0)
sock1.listen(5) # 设置最大允许连接数,各连接和 Server 的通信遵循 FIFO 原则
while True:
sock, addr = sock1.accept() # 获得一个客户端的连接(阻塞式,只有客户端连接后,下面才执行)
print(f'新线程开始处理客户端 {addr} 的请求数据')
port = addr[1]
# 存入socket_dict
socket_dict[port] = sock
# 新建子线程
client_linten = threading.Thread(target=deal, args=(sock, addr))
client_linten.start() # 启动一个线程
def deal(sock,addr): # 传入外部(sock,addr)
while True: # while循环是为了让对话持续
data = sock.recv(4096).decode('utf-8') # 接收客户端数据并且解码, 一次获取 1024b数据(1k)
# print(data)
if data in ('exit', ' '):
print('客户端发送完毕,已断开连接..')
sock.close()
break
else:
print(f'接收到客户端{addr}发送的信息:%s' % data)
# print(result)
# conn.sendall(b'OK')
if __name__ == '__main__':
socket_dict = {}
# 新建子线程
thread_server = threading.Thread(target=server_bind, args=(socket_dict,))
thread_server.start()
while True:
str_input = input("请输入 (端口号,消息) : ")
segments = str_input.split(',')
# 判断输入格式是否合法
if len(segments) == 2:
if segments[0].isnumeric():
port = int(segments[0])
msg = segments[1]
# 判断套接字是否在于字典里
if port in socket_dict.keys():
sock = socket_dict[port]
sock.send(msg.encode("utf-8"))
客户端:
import socket
import threading
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建 Socket 连接
sock.connect(('127.0.0.1', 8001)) # 连接服务器 127.0.0.1是本地ip
def listen_server_txt():
while True: # while循环是为了让对话持续
data = sock.recv(4096).decode('utf-8') # 接收客户端数据并且解码, 一次获取 1024b数据(1k)
print('接收到服务端发送的信息:%s' % data)
if __name__ == '__main__':
listen_server = threading.Thread(target=listen_server_txt, ) #创建一个进程专门监听来自于服务端的信息
listen_server.start()
while True:
input_str = input('请输入发送的内容:')
if input_str == 'exit': #判断退出
sock.send(input_str.encode("utf-8"))
sock.close()
break
if not input_str: #判断文本为空时,跳出本次循环并重新开始循环
print("发送内容不得为空")
continue
sock.send(input_str.encode("utf-8"))
本次任务实现了服务端与客户端的双向通信,在连接了三个客户端的同时,服务端也能向指定服务端发送消息,代码量不大,只要认识了通信的规则,就能轻松掌握。