scoket套接字
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口
服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束
scoket模块
import socket # 生成对象 server = socket.socket() # 绑定ip和端口 server.bind(('127.0.0.1',8080)) #绑定参数为一个元组 #127.0.0.1这叫回送地址 表示电脑本身 端口 为普通整数 一般为8000以上 # 监听 半连接池 server.listen(5) # 接收链接请求 # 第一个是表示客户端的socket 第二个客户端的地址信息 conn,addr = server.accept() #接收字节 data = conn.recv(1024) print(data) # 发送数据 conn.send(b'hello world') # 关闭 conn.close() # 关机 server.close()
import socket client = socket.socket() # 连接服务端 client.connect(('127.0.0.1',8080)) #发送数据 client.send(b'ni hao') # 接收数据 data = client.recv(1024) print(data) # 关闭 client.close()
accept 是一个阻塞函数 会一直等到有客户端链接过来 在继续执行
connect本质实在进行三次握手 也是一个数据传输的过程 如果服务器没有立马响应 也会阻塞
send
发送数据 本质上是把数据交给操作系统来进行发送 一旦数据交给了操作系统 后续的发送
应用程序就无法控制了 ,send一般不会卡 当然 如果数据量很大就会阻塞
scoket套接字是从操作系统缓存区读取数据 如果当前还没有任何数据, 就会进入阻塞会一直等到有数据到来 再继续执行
send与recv对应,不要出现两边都是相同的情况
异常 # 如果已经开启了服务器 再次运行将会抛出 端口占用异常 把之前开启的服务器关掉即可 1.可能是由于你已经启动了服务器程序,却又再次启动了服务器程序,同一个端口不能被多个进程使用导致! 2.三次握手或四次挥手时,发生了异常导致对方程序已经结束而服务器任然处于time_wait状态导致! 3.在高并发的场景下,由于链接的客户端太多,也会产生大量处于time_wait状态的链接
升级版
import socket # 产生对象 server = socket.socket() # 绑定ip和端口 server.bind(('127.0.0.1',8080)) # 监听 server.listen(5) while True: # 接收 conn,addr = server.accept() # print(conn) # print(addr) # 接收 while True: try: data = conn.recv(1024) print(data) # 防止客户端发生异常或断掉 if len(data) == 0: break conn.send(data.upper()) except ConnectionResetError as a: print(a) break conn.close()
import socket # 产生对象 client = socket.socket() # 绑定 client.connect(('127.0.0.1',8080)) while True: # 输入的信息为字符串类型,传输数据是由二进制数据传送的 msg = input('>>>..').encode('utf-8') # 防止输入的信息为空 if len(msg) == 0: continue client.send(msg) data = client.recv(1024) print(data)
TCP粘包问题
TCP流式协议, 数据之间没有分界, 就像水 一杯水和一杯牛奶倒在一起了! UDP 用户数据报协议 粘包 仅发生在TCP协议中 1. 发送端 发送的数据量小 并且间隔短 会粘 2. 接收端 一次性读取了两次数据的内容 会粘 3. 接收端 没有接收完整 剩余的内容 和下次发送的粘在一起 无论是那种情况,其根本原因在于 接收端不知道数据到底有多少
代码
import socket server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) conn ,addr = server.accept() data = conn.recv(1024) print(data) data = conn.recv(10) print(data) data = conn.recv(7) print(data) data = conn.recv(20) print(data)
import socket client = socket.socket() client.connect(('127.0.0.1',8080)) client.send(b'strawberry') client.send(b'berries') client.send(b'interconnection')
# TCP特点
# 会将数据量比较小的并且时间间隔比较短的数据
# 不知到对方要传的数据有多大
# 将数剧打包成固定长度 发送给对方
例子:
import socket import subprocess server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) # 敲入命令行拟cmd实行命令 while True: conn,addr = server.accept() while True: try: cmd = conn.recv(1024) if not len(cmd): break cmd = cmd.decode('utf-8') obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) res = obj.stdout.read() + obj.stderr.read() print(len(res)) conn.send(res) except ConnectionResetError as a: print(a) break conn.close()
import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: cmd = input('>>>').encode('utf-8') if not len(cmd): continue client.send(cmd) data = client.recv(1024) print(data.decode('gbk'))
解决粘包问题 服务端 1.先制作一个发送给客户端的字典 2.制作字典的报头 3.发送字典的报头 4.发送字典 5.再发真实数据 客户端 1.先接受字典的报头 2.解析拿到字典的数据长度 3.接受字典 4.从字典中获取真实数据的长度 5.接受真实数据
如:
import socket import struct import json import subprocess server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) while True: conn,addr = server.accept() while True: try: cmd = conn.recv(1024) if not len(cmd): break cmd = cmd.decode('utf-8') obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) res = obj.stdout.read() + obj.stderr.read() # 一个文件的报头 d = {'name':'baobo','file_size':len(res),'info':'asdfghjwertyuwertzxcvbnmdfghjkejhk'} json_d = json.dumps(d) # 1.先制作一个字典的报头 header = struct.pack('i',len(json_d)) # 2.发送报头长度 conn.send(header) # 3.发送字典 conn.send(json_d.encode('utf-8')) # 4.在发送真实数据 conn.send(res) except ConnectionResetError as a: print(a) break conn.close()
import socket import struct import json client = socket.socket() client.connect(('127.0.0.1',8080)) while True: cmd = input('>>>').encode('utf-8') if not len(cmd): continue client.send(cmd) # 1.先接受字典报头 header_dict = client.recv(4) # 2.解析报头 获取字典的长度 dict_size = struct.unpack('i',header_dict)[0] # 3.接收字典数据 dict_bytes = client.recv(dict_size) dict_json = json.loads(dict_bytes.decode('utf-8')) # 4.从字典中获取信息 print(dict_json) recv_size = 0 real_data = b'' while recv_size < dict_json.get('file_size'): data = client.recv(1024) real_data += data recv_size += len(data) print(real_data.decode('gbk'))
struct模块
struct pack//unpack
python中的struct主要是用来处理C结构数据的,读入时先转换为Python的字符串类型,然后再转换为Python的结构化类型,比如元组(tuple)啥的~。一般输入的渠道来源于文件或者网络的二进制流。
struct.pack()和struct.unpack()
在转化过程中,主要用到了一个格式化字符串(format strings),用来规定转化的方法和格式。
struct.pack(fmt,v1,v2,.....)
将v1,v2等参数的值进行一层包装,包装的方法由fmt指定。被包装的参数必须严格符合fmt。最后返回一个包装后的字符串。
struct.unpack(fmt,string)
解包。比如pack打包,然后就可以用unpack解包了。返回一个由解包数据(string)得到的一个元组(tuple), 即使仅有一个数据也会被解包成元组。