client 客户端 我们用的 需要安装的
server 服务端
browser 浏览器
server 服务端
B/S架构也是C/S架构中的一种
可以离线使用/功能更完善/安全性更高
不用安装就可以使用
统一PC端用户的入口
应用层 python
传输层 port udp tcp 四层路由 四层交换
网络层 ipv4 ipv6 路由器 三层交换机
数据链路层 mac arp协议 网卡 二层交换
物理层
osi五层协议 应用层 传输层 tcp协议 : 效率低 面向连接\可靠\全双工的通信 三次握手 客户端向服务器端发送syn请求, 服务端向客户端回复ack并发送syn请求, 客户端接收到请求之后再回复ack表示建立连接 由客户端的connect + 服务端的accept 四次挥手 客户端向服务端发送fin请求, 服务端回复ack确认 服务端向客户端发送fin请求, 客户端回复ack确认 有客户端的close和服务端的close udp协议 : 效率高 无连接的\不可靠 四层交换机 四层路由器 网络层 ip协议(ipv4 ipv6) 路由器\三层交换机 数据链路层 arp协议 地址解析协议 通过ip找到mac地址 交换机\网卡 : 单播 广播 组播 物理层
七层协议
应用层
表示层
会话层
传输层
网络层
数据链路层
物理层
tcp协议 : 效率低 面向连接\可靠\全双工的通信
udp协议 : 效率高 无连接的\不可靠
客户端向服务器端发送syn请求,
服务端向客户端回复ack并发送syn请求,
客户端接收到请求之后再回复ack表示建立
客户端向服务端发送fin请求,
服务端回复ack确认
服务端向客户端发送fin请求,
客户端回复ack确认
socket套接字充当的就是内置模块的角色
socket 套接字,它存在于传输层与应用层之间的抽象层
import socket sk=socket.socket() #创建一个server端口 sk.bind(("127.0.0.1",9001)) #申请操作系统的资源 sk.listen() #开始监听(可以接收)客户端给我的连接 conn,addr=sk.accept() #创建连接 conn是连接信息 addr是端口号 conn.send(b"hello") #发送内容 msg=conn.recv(1024) #收到字节 print(msg) conn.close() #挥手 断开连接 sk.close () #申请归还的操作系统资源
server
import socket sk=socket.socket() #创建一个server端口 sk.bind(("127.0.0.1",9001)) #申请操作系统的资源 sk.listen() #开始监听(可以接收)客户端给我的连接 conn,addr=sk.accept() #创建连接 conn是连接信息 addr是端口号 conn.send(b"hello") #发送内容 msg=conn.recv(1024) #收到字节 print(msg) conn.close() #挥手 断开连接 sk.close () #申请归还的操作系统资源
client
import socket sk=socket.socket() sk.connect(("127.0.0.1",9001)) msg=sk.recv(1024) print(msg) sk.send(b"beybey") sk.close()
server
import socket sk=socket.socket() sk.bind(("127.0.0.1",9001))# 申请操作系统资源 sk.listen() while True: #为了和多个客户端进行握手 conn,addr=sk.accept() while True: send_msg=input(">>>") conn.send(send_msg.encode("utf-8")) if send_msg.upper()=="Q": break msg=sk.recv(1024).decode("utf-8") if msg.upper()=="Q":break print(msg) conn.close() sk.close() # str -encode(‘utf-8‘)-> bytes # str -encode(‘gbk‘)-> bytes
client
import socket sk = socket.socket() sk.connect(("127.0.0.1",9001)) while True msg=sk.recv(1024) msg2=msg.decode("utf-8") if msg2.upper()=="Q":break print(msg,msg2) send_msg=input(">>>") sk.send(send_msg.encode("utf-8")) if send_msg.upper()=="Q":break sk.close()
注意:UDP协议不用sk.close()
server两发两收
import socket sk = socket.socket(type = socket.SOCK_DGRAM) sk.bind((‘127.0.0.1‘,9001)) while True: # print(sk.recv(1024)) # b‘Your message‘ # print(sk.recvfrom(1024)) # (b‘Your message‘, (‘127.0.0.1‘, 60182)) 注意这个本身是个大元组 recv_msg,addr = sk.recvfrom(1024) # 如果这里用recv,能接收信息,但是无法获得对方IP地址和端口号。所以这里要用recvfrom同时获得消息和地址 print(recv_msg.decode(‘utf-8‘)) send_msg = input(‘>>>‘) sk.sendto(send_msg.encode(‘utf-8‘),addr) improt socket sk=socket.socket(type = socket.SOCK_DGRAM) sk.bind(("127.0.0.1",9001)) while True: msg,addr=sk.recvfrom(1024) print(msg.decode("utf-8")) msg=input(">>>") sk.sendto(msg.encode(‘utf-8‘),addr)
client:三发三收,都先if
import socket sk = socket.socket(type=socket.SOCK_DGRAM) server = (‘127.0.0.1‘,9001) while True: send_msg = input(‘>>>‘) if send_msg.upper() == ‘Q‘: break sk.sendto(send_msg.encode(‘utf-8‘),server) recv_msg = sk.recv(1024).decode(‘utf-8‘) #此时已经有对面的IP地址和端口了,无须再recvfrom if recv_msg.upper() == ‘Q‘: break print(recv_msg)
上述程序中导致程序阻塞的语句
input():等待,直到用户输入enter键 sk.accept():阻塞,有客户端来和我建立完连接之后 sk.connect:阻塞,直到server端结束了对一个client的服务,开始和当前client建立连接的时候 recv():阻塞,直到收到对方发过来的消息之后 recvfrom():阻塞,直到收到对方发过来的消息之后
粘包:两条或更多条分开发送的数据连在一起的现象
粘包导致的原因:
1. 发送端 : 两条消息都很短,发送的间隔时间也非常短,由于优化机制就合并在一起发送了
2. 接收端 : 多条消息由于没有及时接收,而在接收方的缓存端堆在一起导致的粘包
import socket sk = socket.socket() sk.bind((‘127.0.0.1‘,9001)) sk.listen() conn,addr = sk.accept() msg1 = input(‘>>>‘).encode(‘utf-8‘) #注意要在len前先encode msg2 = input(‘>>>‘).encode(‘utf-8‘) num = str(len(msg1)) ret = num.zfill(4) conn.send(ret.encode(‘utf-8‘)) conn.send(msg1) conn.send(msg2) conn.close() sk.close()
import socket sk = socket.socket() sk.connect((‘127.0.0.1‘,9001)) length = int(sk.recv(4).decode(‘utf-8‘)) msg1 = sk.recv(length) msg2 = sk.recv(1024) print(msg1.decode(‘utf-8‘)) print(msg2.decode(‘utf-8‘)) sk.close()
精髓在于把第一条数据精确地规定成4字节,由于第一条数据是第二条数据的长度,因此第二条和第三条数据也不粘了。但是,当第二条数据长度很长,即第一条数据用4字节表示不下时,此方法失效,由此引出第二种方法——struct模块
struct.pack():把-2**23到2**23
范围内的整数转化成4字节bytes
import struct num1 = 129469649 num2 = 123 num3 = 8 ret1 = struct.pack(‘i‘,num1) print(len(ret1)) ret2 = struct.pack(‘i‘,num2) print(len(ret2)) ret3 = struct.pack(‘i‘,num3) print(len(ret3)) print(struct.unpack(‘i‘,ret1)) print(struct.unpack(‘i‘, ret2)) print(struct.unpack(‘i‘, ret3))
server
import struct import socket sk = socket.socket() sk.bind((‘127.0.0.1‘,9001)) sk.listen() conn,addr = sk.accept() msg1 = input(‘>>>‘).encode(‘utf-8‘) #注意要在len前先encode msg2 = input(‘>>>‘).encode(‘utf-8‘) #使用struct.pack()把len(msg1)转化成固定长度为4的bytes blen = struct.pack(‘i‘,len(msg1)) conn.send(blen) conn.send(msg1) conn.send(msg2) conn.close() sk.close() # 粘包现象 # 只出现在tcp协议中,因为tcp协议 多条消息之间没有边界,并且还有一大堆优化算法 # 发送端 : 两条消息都很短,发送的间隔时间也非常短 # 接收端 : 多条消息由于没有及时接收,而在接收方的缓存短堆在一起导致的粘包 # 解决粘包问题的本质 :设置边界
client
import struct import socket sk = socket.socket() sk.connect((‘127.0.0.1‘,9001)) #使用struct.unpack()把接收到的4字节bytes转化成一个元组,元组的第一项就是即将接收的msg1的长度,进而保证了msg1和msg2不会粘在一起 length = struct.unpack(‘i‘,sk.recv(4))[0] msg1 = sk.recv(length) msg2 = sk.recv(1024) print(msg1.decode(‘utf-8‘)) print(msg2.decode(‘utf-8‘)) sk.close()
原文:https://www.cnblogs.com/Pythonzrq/p/11346952.html