tcp服务器
v1
import socket# 创建socket对象
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM tcp协议 SOCK_DGRAM 数据报协议 # 每次都会重用绑定好的ip+端口,使用后不会出现端口占用问题,在不熟悉套接字之前不要使用
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 绑定地址,127.0.0.1也可不填为空
# sk.bind(('', 5000)) # 与下相同
sk.bind(('127.0.0.1', 5000))# 监听连接请求
sk.listen(5) # 半连接池大小
print('服务端启动成功,在5000端口等待客户端连接')# 取出连接请求,开始服务
conn,addr = sk.accept()
print('连接对象:',conn)
print('客户端ip+端口:',addr)# 数据传输
data = conn.recv(1024) # 接收
data = data.decode('utf-8')
print('客户端发送的数据:',data)conn.send(data.upper().encode('utf-8')) # 发送# 结束服务
conn.close()# 关闭服务端
# sk.close()
v2
import socket# 创建socket对象
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM tcp协议 SOCK_DGRAM 数据报协议 # 每次都会重用绑定好的ip+端口,使用后不会出现端口占用问题,在不熟悉套接字之前不要使用
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 绑定地址
sk.bind(('127.0.0.1', 5000))# 监听连接请求
sk.listen(5) # 半连接池大小
print('服务端启动成功,在5000端口等待客户端连接')# 取出连接请求,开始服务
conn,addr = sk.accept()
print('连接对象:',conn)
print('客户端ip+端口:',addr)# 数据传输
while True:data = conn.recv(1024) # 接收data = data.decode('utf-8')if data == 'q':breakprint('客户端发送的数据:',data)conn.send(data.upper().encode('utf-8')) # 发送# 结束服务
conn.close()# 关闭服务端
# sk.close()
v3
import socket# 创建socket对象
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM tcp协议 SOCK_DGRAM 数据报协议 # 每次都会重用绑定好的ip+端口,使用后不会出现端口占用问题,在不熟悉套接字之前不要使用
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 绑定地址
sk.bind(('127.0.0.1', 5000))# 监听连接请求
sk.listen(5) # 半连接池大小
print('服务端启动成功,在5000端口等待客户端连接')# 取出连接请求,开始服务
conn,addr = sk.accept()
print('连接对象:',conn)
print('客户端ip+端口:',addr)# 数据传输
while True:try:data = conn.recv(1024) # 接收except:break# Linux和mac系统上使用的退出if not data:breakdata = data.decode('utf-8')print('客户端发送的数据:',data)conn.send(data.upper().encode('utf-8')) # 发送# 结束服务
conn.close()# 关闭服务端
# sk.close()
v4
import socket# 创建socket对象
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM tcp协议 SOCK_DGRAM 数据报协议 # 每次都会重用绑定好的ip+端口,使用后不会出现端口占用问题,在不熟悉套接字之前不要使用
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 绑定地址
sk.bind(('127.0.0.1', 5000))# 监听连接请求
sk.listen(5) # 半连接池大小
print('服务端启动成功,在5000端口等待客户端连接')# 取出连接请求,开始服务
conn,addr = sk.accept()
print('连接对象:',conn)
print('客户端ip+端口:',addr)# 数据传输
while True:try:data = conn.recv(1024) # 接收except:break# Linux和mac系统上使用的退出if not data:breakdata = data.decode('utf-8')print('客户端发送的数据:',data)conn.send(data.upper().encode('utf-8')) # 发送# 结束服务
conn.close()# 关闭服务端
# sk.close()
v5
import socket# 创建socket对象
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM tcp协议 SOCK_DGRAM 数据报协议 # 每次都会重用绑定好的ip+端口,使用后不会出现端口占用问题,在不熟悉套接字之前不要使用
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 绑定地址
sk.bind(('127.0.0.1', 5000))# 监听连接请求
sk.listen(5) # 半连接池大小
print('服务端启动成功,在5000端口等待客户端连接')# 取出连接请求,开始服务
# 服务多个对象
while True:conn,addr = sk.accept()print('连接对象:',conn)print('客户端ip+端口:',addr)# 数据传输while True:try:data = conn.recv(1024) # 接收except:break# Linux和mac系统上使用的退出if not data:breakdata = data.decode('utf-8')print('客户端发送的数据:',data)conn.send(data.upper().encode('utf-8')) # 发送# 结束服务conn.close()# 关闭服务端
# sk.close()
并发手动实现
# 进程实现
import socket
from multiprocessing import Process# socket.socket(socket.AF_INET,socket.SOCK_STREAM) #流式协议, TCP协议
s = socket.socket() # 同上# 每次都会重用绑定好的ip+端口,使用后不会出现端口占用问题,在不熟悉套接字之前不要使用
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s.bind(('127.0.0.1',5000))s.listen(5)def task(conn):while True:try:data = conn.recv(1024)except:breakif not data:breakprint(data.decode('utf-8'))conn.send(data.upper())conn.close()if __name__ == "__main__":while True:conn, addr = s.accept()p = Process(target=task,args=(conn,))p.start()# 线程实现
import socket
from threading import Thread# socket.socket(socket.AF_INET,socket.SOCK_STREAM) #流式协议, TCP协议
s = socket.socket() # 同上# 每次都会重用绑定好的ip+端口,使用后不会出现端口占用问题,在不熟悉套接字之前不要使用
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s.bind(('127.0.0.1',5000))s.listen(5)def task(conn):while True:try:data = conn.recv(1024)except:breakif not data:breakprint(data.decode('utf-8'))conn.send(data.upper())conn.close()if __name__ == "__main__":while True:conn, addr = s.accept()p = Thread(target=task,args=(conn,))p.start()
面向对象编程方法
# 多进程web服务器
import socket
import multiprocessing
import sysclass StaticWebServer(object):# 初始化StaticWebServer对象,创建socket实例对象,并放入StaticWebServer对象当中def __init__(self,port:int) -> None:self.server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)self.server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)self.server.bind(('',port))self.server.listen(128)# 启动函数def start(self):# 循环等待客户端连入while True:client,ip_port = self.server.accept()print(ip_port[0],"通过",ip_port[1],"连接成功")# 设置多进程,将服务分发给其他进程p = multiprocessing.Process(target=self.task,args=(client,))p.start()client.close()# 关闭字符套接字self.server.close# 处理函数def task(self,client):# 设置接收请求,且请求最大字节为1024request_data = client.recv(1024).decode()# 字符判断如果是0直接退出if len(request_data) == 0:client.close()# 将请求的位置进行截取出else:request_path = request_data.split(' ')[1]print('请求的位置是',request_path)# 字符判断如果是"/"根目录,则path为'/index.html'if request_path == '/':request_path = '/index.html'# 尝试打开文件返回,打开成功执行else,失败执行except,最后执行finallytry:with open('static' + request_path , 'rb') as file:file_content = file.read()except Exception as e:response_line = 'HTTP/1.1 404 NOT FOUND\r\n'response_head = 'Server: PSWS1.1\r\n'with open('static/erroe.html','r',encoding="utf-8") as f:error_data = f.read()response_data = (response_line + response_head + '\r\n' + error_data).encode('utf-8')client.send(response_data)else:response_line = 'HTTP/1.1 200 OK\r\n'response_head = 'Server: PSWS1.1\r\n'with open('static' + request_path , 'rb') as f:response_body = f.read()response_data = (response_line + response_head + '\r\n').encode('utf-8') + response_bodyclient.send(response_data)finally:client.close()if __name__ == "__main__":# 用sys库给port赋值,第0个是执行的文件,取第1个赋值给portif len(sys.argv) != 2:print('输入错误')exit(1)if sys.argv[1].isdigit():port = int(sys.argv[1])StaticWebServer(port).start()else:print('输入错误')# 多线程web服务器
import socket
import threadingclass ScoketServer(object):def __init__(self,port) -> None:# 创建socket实例,参数1代表IPv4,参数2代表tcpself.tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)# 设置端口号复用,程序退出端口号立即释放self.tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)# 对socket指定连接对象,一个人对象是一个元组包含IP地址和端口号,空IP为本地self.tcp_server_socket.bind(("",port))# 设置监听,最大等待128个,设置listen后为被动模式,不能connect主动连接self.tcp_server_socket.listen(128)# 客户端的启动函数def start(self):self.service_client_socket.accept()# 循环等到客户端的连接请求while True:# 等待客户端的连接service_client_socket,ip_port = self.tcp_server_socket.accept()print("客户端连接上来",ip_port)# 创建子线程,将连接上来的客户端放入,子线程操作接收客户端的数据# 设置守护主线程sub_thread = threading.Thread(target=self.handle_client_request,args=(service_client_socket,ip_port),daemon=True)# 开启子线程sub_thread.start()# 关闭tcp服务器的套接字tcp_server_socket.close()# 处理函数,当客户端连接上后进行交互def handle_client_request(self,service_client_socket,ip_port):# 循环接收客户端发送的请求while True:# 接收请求,且请求最大为1024recv_data = service_client_socket.recv(1024)# 判断客户端是否存活,使用if语句对数据进行判断,有数据存活,没有数据死亡if recv_data:print(recv_data.decode('gbk'),ip_port)service_client_socket.send("ok,问题正在处理...".encode("gbk"))else:print("客户端下线",ip_port)break# 终止客户端的连接service_client_socket.close()if __name__ == "__main__":# 携带数据,实例一个ScoketServerserver = ScoketServer(8080)# 运行ScoketServer下的start函数server.start()
并发socketserver
import socketserverclass RequestHandle(socketserver.BaseRequestHandler):def handle(self): # 这个函数的名字不能改变# 这个是一个连接对象,针对于tcp服务,udp没有连接print(self.request) # self.request => connprint(self.client_address)# 数据传输while True:try:data = self.request.recv(1024)except:breakif not data:breakdata = data.decode('utf-8')print('客户端发送的数据',data)self.request.send(data.upper().encode('utf-8'))# 结束服务self.request.close()'''
Threading线程的意思
Forking进程的意思 windwos系统上不支持多进程,因为创建多进程需要调用系统接口进程调用的是 os.fork()这个接口,而os.fork()是针对unix系统发起的造进程的系统调用windows系统多进程调用的不是这几个接口。
'''
sk = socketserver.ThreadingTCPServer(('127.0.0.1',5000),RequestHandle) # 多线程
sk.serve_forever() # => while True: conn,addr = sk.accept() 只负责从半连接池中获取对象,每获取一个对象就会启动一个线程
并发非阻塞IO手动
import socketserver = socket.socket()
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
server.bind(('127.0.0.1',8080))
server.listen(5)
server.setblocking(False) # 所有的网络阻塞都会变成非阻塞c_list = []
d_lis = []
while True:try:conn, addr = server.accept()c_list.append(conn)except BlockingIOError:for conn in c_list:try:data = conn.recv(1024)if not data:conn.close()d_lis.append(conn)conn.send(data.upper())except BlockingIOError:passexcept ConnectionResetError:conn.close()d_lis.append(conn)for conn in d_lis:c_list.remove(conn)d_lis.clear()
并发selectors
import socket
import selectorsdef accept(server):conn,addr = server.accept()sel.register(conn,selectors.EVENT_READ,read)def read(conn):try:data = conn.recv(1024)if not data:conn.close()sel.unregister(conn)returnconn.send(data.upper())except ConnectionResetError:conn.close()sel.unregister(conn)returnserver = socket.socket()
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
server.bind(('127.0.0.1',8080))
server.listen(5)
server.setblocking(False)sel = selectors.DefaultSelector()
sel.register(server,selectors.EVENT_READ,accept)while True:events = sel.select() # linux/mac sel.epoll()for key,mask in events:callback = key.datacallback(key.fileobj)
并发异步IO手动
import socket
import asyncioasync def waiter(conn,loop):while True:try:data = await loop.sock_recv(conn,1024)if not data:breakawait loop.sock_sendall(conn,data.upper())except ConnectionResetError:breakconn.close()async def main(ip,port):server = socket.socket()server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)server.bind((ip,port))server.listen(5)server.setblocking(False) # 非阻塞loop = asyncio.get_running_loop()while True:conn, addr = await loop.sock_accept(server)# 创建task任务loop.create_task(waiter(conn,loop))asyncio.run(main('127.0.0.1',8000))
并发异步IOuvloop
import socket
import asyncio
# import uvloop # 目前不支持windows
# asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) # 但是将asyncio修改为uvloop只需要添加这一行代码即可
# pip install uvloopasync def waiter(conn,loop):while True:try:data = await loop.sock_recv(conn,1024)if not data:breakawait loop.sock_sendall(conn,data.upper())except ConnectionResetError:breakconn.close()async def main(ip,port):server = socket.socket()server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)server.bind((ip,port))server.listen(5)server.setblocking(False) # 非阻塞loop = asyncio.get_running_loop()while True:conn, addr = await loop.sock_accept(server)# 创建task任务loop.create_task(waiter(conn,loop))asyncio.run(main('127.0.0.1',8000))
tcp客户端
v1
import socket# 创建socket对象
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM tcp协议 SOCK_DGRAM 数据报协议 # 建立连接
sk.connect(('127.0.0.1', 5000))# 传输数据
msg = input('请输入>>').strip()
sk.send(msg.encode('utf-8'))data = sk.recv(1024) # 接收
print('客户端发送的数据:',data.decode('utf-8'))# 关闭连接
sk.close()
v2
import socket# 创建socket对象
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM tcp协议 SOCK_DGRAM 数据报协议 # 建立连接
sk.connect(('127.0.0.1', 5000))# 传输数据
while True:msg = input('请输入>>').strip()sk.send(msg.encode('utf-8'))if msg == 'q':breakdata = sk.recv(1024) # 接收print('客户端发送的数据:',data.decode('utf-8'))# 关闭连接
sk.close()
v3
import socket# 创建socket对象
sk = socket.socket() # SOCK_STREAM tcp协议 SOCK_DGRAM 数据报协议 # 建立连接
sk.connect(('127.0.0.1', 5000))# 传输数据
while True:msg = input('请输入>>').strip()sk.send(msg.encode('utf-8'))# 防止发空if not msg:continueif msg == 'q':breakdata = sk.recv(1024) # 接收print('客户端发送的数据:',data.decode('utf-8'))# 关闭连接
sk.close()
udp服务器
import socket# 创建socket对象
sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # SOCK_STREAM tcp协议 SOCK_DGRAM 数据报协议(udp协议) # 每次都会重用绑定好的ip+端口,使用后不会出现端口占用问题,在不熟悉套接字之前不要使用
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 绑定地址
sk.bind(('127.0.0.1', 5000))# 取出连接请求,开始服务# 数据传输
while True:data, addr = sk.recvfrom(1024) # 接收print('客户端发送的数据:',data.decode())sk.sendto(data.upper(),addr) # 发送# 关闭服务端
# sk.close()
udp客户端
import socket# 创建socket对象
sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # SOCK_STREAM tcp协议 SOCK_DGRAM 数据报协议 # 传输数据
while True:msg = input('请输入>>').strip()sk.sendto(msg.encode('utf-8'),('127.0.0.1',5000))if msg == 'q':breakdata, addr = sk.recvfrom(1024) # 接收print('客户端发送的数据:',data.decode('utf-8'))# 关闭连接
sk.close()