网络编程
一、网络基础
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 同一台机器上的两个程序之间的通讯 就需要依赖文件 # 两台机器之间的两个程序之间的通讯 就需要依赖网络 # 两个程序之间的通信: # 基于文件的 # 基于网络的 # 网卡 :身份证 mac地址 计算机在网络上的身份证 # 交换机 :负责一个网络内的多台机器之间的信息交换 # mac地址 :16进制的数 全球唯一 # 区域性 # ip地址 # 4位的点分十进制数 IPV4 # 192.168.10.xxx # 0-255.0-255.0-255.0-255 # 00000000.00000000.00000000.00000000 # 11111111.11111111.11111111.11111111 # IPV6 # 6位的点分十进制数 # 0.0.0.0.0.0 - 255.255.255.255.255.255 # 127.0.0.1 :本地回环地址 本机的地址 # 0.0.0.0 : ip地址的、回环地址的所有的用户都能找到你这台机器 # 每台机器有两个地址 :mac ip地址 # 全球的机器 都连在一块儿 # 局域网 # 网关ip 不同局域网之间通信依赖的ip地址 # 子网掩码 判断两个ip地址是否在同一个网段内 # 网段 # 192.168.16.11 # 255.255.255.0 # 192.168.16.0 # 192.168.16.115 # 11000000.10101000.00001011.10011011 # 11111111.11111111.11111111.00000000 # 11000000.10101000.00001011.00000000 = 192.168.16.0 # 0.0.0.0 - 255.255.255.255 # 局域网的概念 # 外网ip 我们谁都能访问 # 内网ip 从外部不能访问,只能在内部环境中互相访问 # 外网ip永远不会和内网ip冲突? # 0.0.0.0 - 255.255.255.255中间为内网保留了一些字段 # 192.168.0.0 - 192.168.255.255 # 10.0.0.0 - 10.255.255.255 # 172.16.0.0 - 172.31.255.255 # 127.0.0.1 回环地址 指的是在我们的测试过程中使用的一个地址 # 0.0.0.0 开发环境中 # 端口的概念 —— 帮助你找到一个应用 # 每一个网络服务都会占用计算机上的一个端口 # 计算机上的端口范围 0-65535 # 在同一时刻 同一台计算机上 不同的网络应用 占用的端口一定是不同的 # osi七层模型 # 应用层 http、https # 传输层 TCP、UDP协议 # 网络层 ip协议 # 数据链路层 arp协议 # 物理层 # tcp 面向连接的 可靠的 但是慢 # tcp协议 # 两个应用之间要想通信 必须先建立连接 # 然后基于连接来通信 # 比较重要的文件 邮件的发送 下载安装包 # udp 无连接的 快 能够发送的信息长度是有限的 # 快 但 不可靠 不能发送过长的数据 # 即时通讯类的程序 # 局域网 # mac地址 # ip地址 # 交换机 # arp协议 通过ip地址找到mac地址 广播 单播 # 局域网之间的通信 # 路由器 # 网段、子网掩码 # 网关ip # 端口 # 应用到应用 # tcp、udp是通过网络通信的两种方式 # tcp 先建立连接再通信 # 可靠 # 慢 # udp 不需要建立连接直接通信 # 快 # 不可靠 # osi七层协议 # 应用层 # 传输层 tcp\udp协议 # 网络层 ipv4\ipv6 # 数据链路层 arp协议 # 物理层
二、socket编程
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#基于tcp的socket #server import socket sk = socket.socket() sk.bind(('127.0.0.1',9000)) sk.listen() while 1: conn,addr = sk.accept() print(conn,addr) while 1: msg = input('<<<').strip() conn.send(msg.encode('utf-8')) if msg == 'q': break re_msg = conn.recv(1024) if re_msg == b'q': break print(re_msg.decode('utf-8')) conn.close() sk.close() #client import socket sk = socket.socket() sk.connect(('127.0.0.1',9000)) while 1: msg = sk.recv(1024) if msg == b'q': break print(msg.decode('utf-8')) send_msg = input('<<<') sk.send(send_msg.encode('utf-8')) if send_msg == 'q': break sk.close() #基于udp的socket #server import socket sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(('127.0.0.1',9000)) while 1: msg,addr = sk.recvfrom(1024) name,content = msg.decode('utf-8').split('|') print('%s:%s' %(name,content)) send_msg = input('<<<').strip() if send_msg == 'q': break send_msg = '%s|%s' %('server',send_msg) sk.sendto(send_msg.encode('utf-8'),addr) sk.close() #client import socket sk = socket.socket(type=socket.SOCK_DGRAM) while 1: send_msg = input('<<<').strip() if send_msg == 'q': break send_msg = '%s|%s' %('zfy',send_msg) sk.sendto(send_msg.encode('utf-8'),('127.0.0.1',9000)) msg,addr = sk.recvfrom(1024) name,content = msg.decode('utf-8').split('|') print('%s:%s' %(name,content)) sk.close()
三、粘包现象
粘包现象
1、tcp是流式传输,字节流,数据与数据之间是没有边界的。
流式传输不限定长度,可靠传输
慢,和一个人的通信连接conn会一直占用我们的通信资源
2、udp协议是面向数据包的传输,不能传输过长的数据,不可靠。
快,由于不需要建立连接,所以谁发消息我都能收到
粘包:由于流式传输的特点,产生了数据连续发送的粘包现象
在一个conn建立起来的连接上传输的多条数据是没有
边界的,数据的发送和接收实际上不是在执行send和recv
的时候就立刻被发送或者接收,而是需要经过操作系统内核
Nagle算法,能够将发送间隔时间很近的短数据合成一个包
发送到接收端
拆包机制:当要发送的数据超过了网络上能传输的最大长度
就会被tcp协议强制拆包
如果是短数据:只需要告诉对方边界就可以了
如果是长数据:不仅要告诉对方边界,还要保证对方完整的接收了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#解决粘包问题 #server import socket import struct sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM) ip = '127.0.0.1' port = 9000 sk.bind((ip,port)) sk.listen() while 1: print('wait connecting....') conn,addr = sk.accept() while 1: send_msg = input('<<<').strip().encode('utf-8') pack_num = struct.pack('i',len(send_msg)) conn.send(pack_num) conn.send(send_msg) if send_msg.upper() == b'Q':break pack_num = conn.recv(4) num = struct.unpack('i',pack_num)[0] msg = conn.recv(num) if msg == b'q':break print(msg.decode('utf-8')) conn.close() sk.close() #client import socket import struct sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM) sk.connect(('127.0.0.1',9000)) while 1: pack_num = sk.recv(4) num = struct.unpack('i',pack_num)[0] msg = sk.recv(num) if msg == b'q':break print(msg.decode('utf-8')) send_msg= input('<<<').strip().encode('utf-8') pack_num = struct.pack('i',len(send_msg)) sk.send(pack_num) sk.send(send_msg) if send_msg == b'q':break sk.close()
四、验证客户端连接的合法性
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#server import socket import hmac import os def auth(conn): msg = os.urandom(32) conn.send(msg) # print(msg, type(msg)) result = hmac.new(ser_key,msg) client_digest = conn.recv(1024) if result.hexdigest() == client_digest.decode('utf-8'): print('合法连接') return True else: print('不合法的连接') return False ser_key = b'123qwe' sk = socket.socket() sk.bind(('127.0.0.1',9000)) sk.listen() conn,addr = sk.accept() if auth(conn): print(conn.recv(1024)) conn.close() else: conn.close() sk.close() #client import socket import hmac def auth(sk): msg = sk.recv(32) # print(msg,type(msg)) result = hmac.new(ser_key,msg) res = result.hexdigest() sk.send(res.encode('utf-8')) ser_key = b'123qwe' sk = socket.socket() sk.connect(('127.0.0.1',9000)) auth(sk) sk.send(b'upload') sk.close()
五、socketserver模块
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#server import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): conn = self.request while 1: conn.send(b'hello') print(conn.recv(1024)) socketserver.TCPServer.allow_reuse_address = True server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),MyServer) server.serve_forever() #client import socket sk = socket.socket() sk.connect(('127.0.0.1',9000)) while 1: ret = sk.recv(1024) print(ret) sk.send(b'byebye') sk.close()