基于TCP的套接字,关键就是两个循环,一个链接循环,一个通信循环
socketserver模块中分两大类:server类(解决链接问题)和request类(解决通信问题)
"""
TCP服务端:
服务端要做两件事:
- 第1件事就是循环地从半连接池中取出链接请求与其建立双向链接,拿到链接对象
- 第2件事就是拿到链接对象,与其进行通信循环 ===> handle
import socketserver
class MyHandler(socketserver.BaseRequestHandler):
def handle(self): # 必须要写. 里面放的就是我们的通讯循环. 用来存放与客户端进行通信的逻辑代码.
self.request
- 如果是TCP协议,拿到的就是一个客户端的套接字对象conn
self.client_address
- 以('ip', 端口)二元组的形式表示, Ip和端口都是客户端的.
补充: socketserver.ForkingTCPServer Windows系统中不支持.
server = socketserver.ThreadingTcpServer(('IP', 端口), 自定义的类)
- 第1个参数: 指定服务端所绑定的Ip和端口, 这里的Ip和端口目的是提供客户端进行访问.
- 第2个参数: 指定我们自定义的那个类里面定义我们的通信循环.
server.serve_forever()
- 永久提供服务. 这是一个死循环的过程,对应的就是我们的链接循环.
"""
import socketserver
class MyHandler(socketserver.BaseRequestHandler): # 必须继承这个类来使用它的功能
def handle(self): #用于通信循环
print(self.request)
print(self.client_address)
# 通信循环
while True:
try:
data = self.request.recv(1024)
if not data: break
self.request.send(data.upper())
except ConnectionResetError:
break
self.request.close()
if __name__ == '__main__':
#做绑定IP和端口并设置监听的事,"bind_and_activate" 默认等于 "True"
s = socketserver.ThreadingTCPServer(('127.0.0.1', 8888), MyHandler, bind_and_activate=True)
s.serve_forever() # 代表循环连接通讯
# 循环建立连接,每建立一个连接就会启动一个线程(服务员)+调用Myhanlder类产生一个对象,调用该对象下的handle方法,专门与刚刚建立好的连接做通信循环
import socket
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(("127.0.0.1",8888))
while True:
msg=input("请输入你要操作的指令>>>:").strip()
if not msg: continue
client.send(msg.encode("utf-8"))
date_bytes=client.recv(1024)
print(date_bytes)
client.close()
import socket
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(("127.0.0.1",8888))
while True:
msg=input("请输入你要操作的指令>>>:").strip()
if not msg: continue
client.send(msg.encode("utf-8"))
date_bytes=client.recv(1024)
print(date_bytes)
client.close()
"""
UDP服务端:
import socketserver
class MyRequestHandle(socketserver.BaseRequestHandler):
def handle(self): # 必须要写. 里面放的就是我们的通讯循环. 用来存放与客户端进行通信的逻辑代码.
self.request 拿到的是一个元组
- 第1个参数是客户端发送过来的数据,
- 第2个参数是拿到的客户端的套接字对象
self.client_address
- 以('ip', 端口)二元组的形式表示, Ip和端口都是客户端的.
server = socketserver.ThreadingUDPServer(('IP', 端口), 自定义的类)
- 第1个参数: 指定服务端所绑定的Ip和端口, 这里的Ip和端口目的是提供客户端进行访问.
- 第2个参数: 指定我们自定义的那个类类里面定义我们的通信循环.
server.serve_forever()
- 只负责循环的收,以后包括收发在内后续的数据,都交给线程去处理.
"""
import socketserver
print("Wait...")
class MyRequestHanlder(socketserver.BaseRequestHandler):
def handle(self):
print(self.request)
print(self.client_address)
data_bytes, conn = self.request
print(f'来自{self.client_address}客户端发送过来的消息: {data_bytes.decode("utf-8")}')
conn.sendto(data_bytes.upper(), self.client_address)
s = socketserver.ThreadingUDPServer(('127.0.0.1', 8080), MyRequestHanlder)
s.serve_forever()
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 流式协议=》tcp协议
while True:
msg = input('请输入你要操作的指令>>>: ').strip()
client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))
data_bytes, server_address = client.recvfrom(1024)
print('data_bytes:', data_bytes)
print('server_address:', server_address)
client.close()
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 流式协议=》tcp协议
while True:
msg = input('请输入你要操作的指令>>>: ').strip()
client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))
data_bytes, server_address = client.recvfrom(1024)
print('data_bytes:', data_bytes)
print('server_address:', server_address)
client.close()
# 加入一条socket配置,重用ip和端口
import socket
from socket import SOL_SOCKET,SO_REUSEADDR
sk = socket.socket()
sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
sk.bind(('127.0.0.1',8888)) #把地址绑定到套接字
sk.listen() #监听链接
conn,addr = sk.accept() #接受客户端链接
ret = conn.recv(1024) #接收客户端信息
print(ret) #打印客户端信息
conn.send(b'hello') #向客户端发送信息
conn.close() #关闭客户端套接字
sk.close() #关闭服务器套接字(可选)