1 .简述socket 通信原理
如上图,socket通信建立在应用层与TCP/IP协议组通信(运输层)的中间软件抽象层,它是一组接口,在设计模式中,socket其实就是一个门面模式,它把复杂的TCP/IP协议组隐藏在Socket接口后面,对于用户来说,一组简单的接口就是全部,让socket去组织数据,以符合指定的协议。
所以,经常对用户来讲,socket就是ip+prot 即IP地址(识别互联网中主机的位置)+port是程序开启的端口号
socket通信如下:
客户端
# _*_ coding: utf-8 _*_ import socket ip_port = ('127.0.0.1',9696) link = socket.socket(socket.AF_INET,socket.SOCK_STREAM) link.connect(ip_port) print("开始发送数据") cmd = input("client请输入要发送的数据>>>>").strip() link.send(cmd.encode('utf-8')) recv_data = link.recv(1024) print("这是受到的消息:",recv_data) link.close()
服务端
# _*_ coding: utf-8 _*_ import socket ip_port = ('127.0.0.1',9696) link = socket.socket(socket.AF_INET,socket.SOCK_STREAM) link.bind(ip_port) link.listen(5) conn,addr = link.accept() #这里,因为我们知道自己写的少,所以1024够用 recv_data = conn.recv(1024) print("这是受到的消息:",recv_data) cmd = input("server请输入要发送的数据>>>>").strip() conn.send(cmd.encode('utf-8')) conn.close() link.close() 2,粘包的原因和解决方法?
2. 粘包的原因和解决方法?
TCP是面向流的协议,发送文件内容是按照一段一段字节流发送的,在接收方看来不知道文件的字节流从和开始,从何结束。
UDP是面向消息的协议,每个UDP段都是一个消息,
直接原因:
所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
根本原因:
发送方引起的粘包是由TCP协议本身造成的,TCP为了提高传送效率,发送方往往要收集到足够多的数据
才发送一个TCP段,若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成到一个TCP
段后一次发送过去,这样接收方就受到了粘包数据。
如果需要一直收发消息,加一个while True即可。但是这里有个1024的问题,即粘包,
粘包的根源在于:接收端不知道发送端将要发的字节流的长度,所以解决粘包问题的方法就是围绕如何让发送端在发送数据前,把自己将要发送的字节流大小让接收段知道,然后接收端来一个死循环,接收完所有的数据即可。
粘包解决的具体做法:为字节流加上自定义固定长度报头,报头中包含字节流长度,然后依次send到对端,对端在接受时,先从缓存中取出定长的报头,然后再取真是数据。
客户端
# _*_ coding: utf-8 _*_ import socket import struct import json phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.connect(('127.0.0.1',8080)) #连接服务器 while True: # 发收消息 cmd = input('请你输入命令>>:').strip() if not cmd:continue phone.send(cmd.encode('utf-8')) #发送 #先收报头的长度 header_len = struct.unpack('i',phone.recv(4))[0] #吧bytes类型的反解 #在收报头 header_bytes = phone.recv(header_len) #收过来的也是bytes类型 header_json = header_bytes.decode('utf-8') #拿到json格式的字典 header_dic = json.loads(header_json) #反序列化拿到字典了 total_size = header_dic['total_size'] #就拿到数据的总长度了 #最后收数据 recv_size = 0 total_data=b'' while recv_size<total_size: #循环的收 recv_data = phone.recv(1024) #1024只是一个最大的限制 recv_size+=len(recv_data) #有可能接收的不是1024个字节,或许比1024多呢, # 那么接收的时候就接收不全,所以还要加上接收的那个长度 total_data+=recv_data #最终的结果 print('返回的消息:%s'%total_data.decode('gbk')) phone.close()
服务端
# _*_ coding: utf-8 _*_ import socket import subprocess import struct import json phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(('127.0.0.1',8080)) #绑定手机卡 phone.listen(5) #阻塞的最大数 print('start runing.....') while True: #链接循环 coon,addr = phone.accept()# 等待接电话 print(coon,addr) while True: #通信循环 # 收发消息 cmd = coon.recv(1024) #接收的最大数 print('接收的是:%s'%cmd.decode('utf-8')) #处理过程 res = subprocess.Popen(cmd.decode('utf-8'),shell = True, stdout=subprocess.PIPE, #标准输出 stderr=subprocess.PIPE #标准错误 ) stdout = res.stdout.read() stderr = res.stderr.read() # 制作报头 header_dic = { 'total_size': len(stdout)+len(stderr), # 总共的大小 'filename': None, 'md5': None } header_json = json.dumps(header_dic) #字符串类型 header_bytes = header_json.encode('utf-8') #转成bytes类型(但是长度是可变的) #先发报头的长度 coon.send(struct.pack('i',len(header_bytes))) #发送固定长度的报头 #再发报头 coon.send(header_bytes) #最后发命令的结果 coon.send(stdout) coon.send(stderr) coon.close() phone.close()
TCP和UDP协议在传输层
4. 简述3次握手,四次挥手?
三次握手:
client发送请求建立通道;
server收到请求并同意,同时也发送请求建通道;
client收到请求并同意,建立完成
四次挥手:
client发送请求断开通道;
server收到请求并同意,同时还回复client上一条消息;
server也发送请求断开通道;
client受到消息结束