简介
- 应用层:为用户提供所需要的各种服务,包括FTP,Telnet,DNS,SMTP等
 - 传输层:TCP和UDP
 - 网络层:IP,IGMP,ICMP
 - 链路层:负责监视数据在主机和网络之间的交换
 
IP协议
负责把数据从一台计算机通过网络发送给另一台计算机。数据被分割成一小块一小块,然后通过IP包发送出去。
不保证都能到达,也不保证顺序到达。
TCP协议
TCP协议建立在ip基础上,负责在俩台计算机之间建立可靠连接,保证数据包按顺序到达,通过3次握手建立可靠连接
创建TCP服务器
流程
- 使用socket创建一个套接字
 - 使用bind绑定IP和端口
 - 使用listen使套接字变为可以被动连接
 - 使用accept等待客户端的连接
 - 使用recv/send接收发送数据
 
具体可参考SOCKET
host = '127.0.0.1'port = 8080web = socket.socket(socket.AF_INET,socket.SOCK_STREAM)web.bind((host,port))web.listen(5)sock,addr = web.accept()print('已经建立好连接')info = sock.recv(1024).decode()while info != 'byebye':if info:print('接收到的内容',info,sep=':')send_data = input('输入发送的内容:')sock.send(send_data.encode())if send_data == 'byebye':breakinfo = sock.recv(1024).decode()sock.close()web.close()
创建客户端
import sockets = socket.socket()host = '127.0.0.1'port = 8080s.connect((host,port))print('已连接')info = ''while info != 'byebye':send_data = input('输入发送的内容:')s.send(send_data.encode())if send_data == 'byebye':breakinfo = s.recv(1024).decode()print('接收到的内容',info,sep=':')s.close()
通信模型
粘包
现象
发送方发送俩个字符串”hello”+”world”,接收方却一次性接收到了”helloworld”
原因
TCP为了提高网络的利用率,会使用一个叫做Nagle的算法。该算法是指,发送端即使有要发送的数据,如果很少的话,会延迟发送。如果应用层给TCP传送数据很快的话,就会把两个应用层数据包“粘”在一起,TCP最后只发一个TCP数据包给接收端。
tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端总是在收到ack时才会清除缓冲区内容。数据是可靠的,但是会粘包。
解决办法
为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据
是有struct模块把一个类型转化为固定长度的bytes
客户端
#! /usr/bin/env python3#conding=utf-8import jsonimport structimport osimport hashlibimport socketcur_path = os.path.dirname(os.path.abspath(__file__))file_name = os.path.join(cur_path,'a.txt')file_size = os.path.getsize(file_name)f = open(file_name,'rb')file_md5 = hashlib.md5(f.read()).hexdigest()#自定义包头header={'file_size':file_size,'file_name':file_name,'md5':file_md5}#序列化包头并转为byteshead_bytes=bytes(json.dumps(header),encoding='utf-8')#用struct将包头长度这个数字转化为固定的长度4字节head_len_bytes=struck.pack('i',len(head_bytes))#客户端开始发送host = '127.0.0.1'port = 8080conn = socket.socket()conn.connect((host,port))conn.send(head_len_bytes) #先发送报头的长度conn.send(head_bytes)#再发送报头的字节格式conn.sendall(f.read()) #再发送真实内容的字节格式
服务器
import socketimport jsonhost = '127.0.0.1'port = 8080web = socket.socket(socket.AF_INET,socket.SOCK_STREAM)web.bind((host,port))web.listen(5)sock,addr = web.accept()head_len_bytes = sock.recv(4) #收报头4个bytes,得到报头长度的字节格式header_len = struck.unpack('i',head_len_bytes)[0] #提取报头的长度head_bytes = sock.recv(header_len) #按照报头的长度接收报头header = json.loads(head_bytes.decode('utf-8')) #解码并反序列化得到报头的json内容real_data_len = header['file_size'] #提取出报头中数据的长度real_data = s.recv(real_data_len) #根据报头中文件的长度获取到实际的文件内容
