python socket运用
一、基于TCP协议的简单套接字程序

import socket server = socket.socket(socket.AF_INET,type = socket.SOCK_STREAM) # 创建服务器套接字 # AF_INET 基于网络类型的套接字家族 # AF_UNIX 基于文件类型的套接字家族 # type=SOCK_STREAM 流式协议指的就是tcp协议 server.bind(('127.0.0.1',8086)) # 绑定ip和端口 请看是元组类型 server.listen(5) # 允许 等待的最大请求数 conn,client_addr = server.accept() # 获取请求的连接对象,和客户端的ip数据 data = conn.recv(1024) # 一次接受最大1024 bytes # 接受客户端发送的数据 send_data = '我再发送给你:'+data.decode('utf-8') print(send_data) conn.send(send_data.encode('utf-8')) conn.close() server.close()

import socket client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) client.connect_ex(('127.0.0.1',8086)) # connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 msg = input('你要发送的东东>>>').strip() client.send(msg.encode('utf-8')) data = client.recv(1024) print(data.decode('utf-8')) # 接受最大 数据1024 bytes client.close()
# 问题: 只能对话一次就结束了 # 如何解决? 请继续阅读
二 、 单一服务端与客户端通讯

#! /usr/bin/env python # -*- coding:utf-8 -*- # Author Jmz import socket server = socket.socket(socket.AF_INET,type = socket.SOCK_STREAM) # 创建服务器套接字 # AF_INET 基于网络类型的套接字家族 # AF_UNIX 基于文件类型的套接字家族 # type=SOCK_STREAM 流式协议指的就是tcp协议 server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 加入一条socket配置,重用ip和端口,如果存在则重用 ip和端口 server.bind(('127.0.0.1',8086)) # 绑定ip和端口 请看是元组类型 server.listen(5) # 允许 等待的最大请求数 conn,client_addr = server.accept() # 获取请求的连接对象,和客户端的ip数据 while True: data = conn.recv(1024) # 一次接受最大1024 bytes # 接受客户端发送的数据 print(data.decode('utf-8')) msg = input('我的回复:').strip() conn.send(msg.encode('utf-8')) conn.close() server.close()

#! /usr/bin/env python # -*- coding:utf-8 -*- # Author Jmz import socket client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) client.connect_ex(('127.0.0.1',8086)) # connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 while True: msg = input('我的发送:').strip() client.send(msg.encode('utf-8')) data = client.recv(1024) print(data.decode('utf-8')) client.close()
# 问题:只能与一个用户进行聊天,聊天结束即进程结束。 # 如何能过服务多个用户?
三、多用户通讯,一个结束后可继续下一个

#! /usr/bin/env python # -*- coding:utf-8 -*- # Author Jmz import socket server = socket.socket(socket.AF_INET,type = socket.SOCK_STREAM) # 创建服务器套接字 # AF_INET 基于网络类型的套接字家族 # AF_UNIX 基于文件类型的套接字家族 # type=SOCK_STREAM 流式协议指的就是tcp协议 server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 加入一条socket配置,重用ip和端口,如果存在则重用 ip和端口 server.bind(('127.0.0.1',8086)) # 绑定ip和端口 请看是元组类型 server.listen(2) # 允许 等待的最大请求数 # 获取请求的连接对象,和客户端的ip数据 while True: conn, client_addr = server.accept() while True: try: data = conn.recv(1024) # 一次接受最大1024 bytes if not data: break # 用于 linux ,当用户端口连接时,发送一个b'' # 接受客户端发送的数据 print(data.decode('utf-8')) msg = input('我的回复:').strip() conn.send(msg.encode('utf-8')) except Exception as e: break # 用于windows ,当客户端断开时,报错 conn.close() server.close()

#! /usr/bin/env python # -*- coding:utf-8 -*- # Author Jmz import socket client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) client.connect_ex(('127.0.0.1',8086)) # connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 while True: try: msg = input('我的发送:').strip() client.send(msg.encode('utf-8')) data = client.recv(1024) if not data:break print(data.decode('utf-8')) except Exception as e: break client.close()

#! /usr/bin/env python # -*- coding:utf-8 -*- # Author Jmz import socket client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) client.connect_ex(('127.0.0.1',8086)) # connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 while True: try: msg = input('我的发送:').strip() client.send(msg.encode('utf-8')) data = client.recv(1024) if not data:break print(data.decode('utf-8')) except Exception as e: break client.close()
# 注意点: 当用户的请求数大于listen的最大限制数时就会结束后面的请求 # 问题:服务端只能在一个用户通信结束后才能进行下一个通讯,无法与多个用了同时聊天 # 多用户===》多进程,后面说道说再说
四、粘包现象
# 所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。 # 此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。
# 若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。

import socket server = socket.socket(socket.AF_INET,type = socket.SOCK_STREAM) # 创建服务器套接字 server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) server.bind(('127.0.0.1',8086)) server.listen(5) conn,client_addr = server.accept() data1 = conn.recv(5) print(data1.decode('utf-8')) data2 = conn.recv(5) print(data2.decode('utf-8')) conn.close() server.close()

import socket client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) client.connect_ex(('127.0.0.1',8086)) client.send(b'h') client.send(b'e') client.send(b'll') client.send(b'o') client.send(b'world') client.close()
# 客户端多次发送数据, # 服务端在接受数据时,多次获取的数据都不能确定
五、粘包问题解决方式
# 方式一(不推荐使用) # 客户端发送数据时可以停顿?秒,这样服务端接受的数据就是发送一次的数据 # 方式二 # 1、报头数据:记录主数据内容大小和主要详情 # 2、将报头数据大小,通过struct模块,转成指定大小的数据(固定长度的bytes数据)在发送 # 3,先接受指定报头数据,在转而接受报头信息,再解码 获取数据内容。
方式二,简单实现ssh

from socket import * import subprocess,struct,json server = socket(AF_INET,SOCK_STREAM) server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) server.bind(('127.0.0.1',8082)) server.listen(5) conn,client_addr=server.accept() while True: try: cmd = conn.recv(1024) if not cmd:break obj = subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout = obj.stdout.read() # 获取的系统默认编码格式的数据 stderr = obj.stderr.read() # 我使用的是Mac 默认utf-8, header_msg = {'cmd':cmd.decode('utf-8'),'total_size':len(stdout)+len(stderr)} header_json = json.dumps(header_msg) header_total = struct.pack('i',len(header_json)) # 将数字转成16进制数的 4bytes 的bytes类型值 conn.send(header_total) # 发送报头 4 bytes 的 数据 conn.send(header_json.encode('utf-8')) # 发送 报头数据 conn.send(stdout+stderr) # 发送内容数据 except Exception as e: break conn.close() server.close()

from socket import * import json,struct client = socket(AF_INET,SOCK_STREAM) client.connect_ex(('127.0.0.1',8082)) while True: msg = input('>>>').strip() if not msg: continue client.send(msg.encode('utf-8')) header_total = client.recv(4) header_msg_size = struct.unpack('i',header_total)[0] header_msg =client.recv(int(header_msg_size)) header_data = json.loads(header_msg) start_size = 0 data = b'' while start_size < int(header_data['total_size']): recv_data = client.recv(1024) data += recv_data start_size+=len(recv_data) print(data.decode('utf-8'))
六、socketserver 多进程socket

import subprocess,struct,json import socketserver # 通讯循环类 class MyTcpHandle(socketserver.BaseRequestHandler): def handle(self): conn=self.request while True: try: cmd = conn.recv(1024) if not cmd:break obj = subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout = obj.stdout.read() stderr = obj.stderr.read() header_msg = {'cmd':cmd.decode('utf-8'),'total_size':len(stdout)+len(stderr)} header_json = json.dumps(header_msg) header_total = struct.pack('i',len(header_json)) # 将数字转成16进制数的 4bytes 的bytes类型值 conn.send(header_total) # 发送报头 4 bytes 的 数据 conn.send(header_json.encode('utf-8')) # 发送 报头数据 conn.send(stdout+stderr) # 发送内容数据 except Exception as e: break if __name__ == '__main__': server = socketserver.ThreadingTCPServer(('127.0.0.1',8083),MyTcpHandle) server.serve_forever()

from socket import * import json,struct client = socket(AF_INET,SOCK_STREAM) client.connect_ex(('127.0.0.1',8083)) while True: msg = input('>>>').strip() if not msg: continue client.send(msg.encode('utf-8')) header_total = client.recv(4) header_msg_size = struct.unpack('i',header_total)[0] header_msg =client.recv(int(header_msg_size)) header_data = json.loads(header_msg) start_size = 0 data = b'' while start_size < int(header_data['total_size']): recv_data = client.recv(1024) data += recv_data start_size+=len(recv_data) print(data.decode('utf-8'))
# socket server 多进程操作,可实现多人同时操作 # 在还未学习进程线程前的必备良品