网络编程自我感觉还是很重要的一部分
网络基础
C/S架构 client/server
B/S架构 server/browser
两者的关系?
B/S架构是C/S架构的一种
计算机与计算机之间是如何通信的?
两台计算机之间的通信
网卡、网线
网卡:提供网线的接口,通过网卡找到计算机,一个网卡拥有全球唯一的mac地址
同一个局域网中多台计算机之间的通信
网卡,网线,交换机
交换机:一个中心节点,所有的网线连接到交换机上
计算机1寻找计算机2并发消息的过程:
计算机1发送消息到交换机,交换机把这个信息广播给局域网中所有计算机,计算机2收到消息与自己的mac地址比对,确认是发给自己的消息之后,回复消息给交换机,交换机发消息到计算机1
不同局域网中多台计算机之间的通信
网卡,网线,交换机,路由器
路由器:连接多个交换机
广播:计算机1要和计算机2通信,告诉交换机要找计算机2,交换机会告诉所有的计算机要找计算机2
广播风暴:所有的计算机同时去找,同一台计算机会接收到很多与自己不相关的信息,从而造成网络拥堵
单播:只有计算机2会回复消息给交换机,交换机发送给计算机1
网关:不同的局域网中的计算机之间通信,同一个局域网的默认网关相同
子网掩码:ip地址和子网掩码按位与 (计算出来局域网的网段,同一个网段只有最后一位不同,前三位都相同)来判断是否在同一局域网
按位与:1|1=1,1|0=1,0|0=0
ip地址
4个点分十进制(4个八位二进制数)范围:0-255
作用:
通过ip地址可以找到对应的mac地址 根据arp协议
127.0.0.1:本地回环地址,本地中的两个程序通信
ip协议
IPV4:0.0.0.0-255.255.255.255
IPV6:0.0.0.0.0.0-255.255.255.255.255.255
作用:1.为一台计算机分配ip地址
2.计算计算机的网段
如果想让别人能访问到自己的计算机中的程序,必须有一个已经在公网中注册的ip地址
端口
计算机里边的每一个需要联网的程序都有一个端口
qq聊天时,对方不仅需要你的ip地址,还需要qq程序的端口号
端口的范围:0-25535
自己开端口:开8000后的端口
互联网协议
每层的工作
应用层 程序,要发送的数据包
传输层 选择通信协议,给要发送的数据加上协议
网络层 给要发送的数据加上ip信息
数据链路层 通过arp协议得到mac地址,给要发送的数据加上mac地址
物理层 两个计算机链接
每层用到的协议:
应用层 http协议,SMTP协议,ftp协议
传输层 TCP/UDP协议
网络层 ip协议
数据链路层 ARP协议
物理层
每层常见物理设备:
应用层
传输层 四层交换机,四层路由器
网络层 路由器,三层交换机(带路由功能)
数据链路层 网桥,交换机,网卡
物理层 中继器,双绞线,集线器
TCP协议/UDP协议
TCP协议:可靠的,建立链接的
全双工通信:双方可以互相收发信息
三次握手:一定是client先请求发送--server端接受请求并且请求发送--client端接收请求 通道连接成功
四次挥手:client/server请求断开连接--server/client同意断开--server/client请求断开连接--client/server同意断开
UDP协议:
不建立链接的,不可靠的
网络编程
使用socket编程来进行进程间的通讯
基于TCP协议的socket编程
TCP服务端:创建套接字 (socket)--绑定端口 (bind)--倾听客户请求(listen)--接受客户端连接(accept)--接受、发送(recv、send)--关闭套接字(closesocket)
TCP客户端:创建套接字(socket)--链接服务器(connect)--发送,接受(send、recv)--关闭套接字(closesocket)
import socket soc = socket.socket() #手机开机 soc.bind(('localhost',9999)) #启动SIM卡 soc.listen() #等待别人打电话 connect,addr = soc.accept() #拿到通话连接,别人的电话号 recv = connect.recv(1024) #听别人讲话 send = connect.send(b'hello') #对别人讲话 print(str(recv)) connect.close() #挂断电话 soc.close() #关机
import socket sock = socket.socket() #手机开机 sock.connect(('localhost',9999)) #给别人打电话 sock.send(b'jcc') #对别人讲话 rec = sock.recv(1024) #听别人讲话 print(str(rec)) sock.close() #关机
基于UDP协议的socket编程
import socket udpsk = socket.socket(type=socket.SOCK_DGRAM) #创建一个服务器的套接字 DGRAM:datagram 基于udp传输的数据包 udpsk.bind(('localhost',8888)) #绑定指定服务器 while 1: msg,addr = udpsk.recvfrom(1024) #接受消息 print(msg.decode('utf-8')) if msg.decode('utf-8')=='bye':break inp = input('>>>') udpsk.sendto(bytes(inp,encoding='utf-8'),addr) #发送消息 udpsk.close() #关闭套接字
import socket udpsk = socket.socket(type=socket.SOCK_DGRAM) #创建一个服务器的套接字 DGRAM:datagram 基于udp传输的数据包 udpsk.bind(('localhost',8888)) #绑定指定服务器 while 1: msg,addr = udpsk.recvfrom(1024) #接受消息 print(msg.decode('utf-8')) if msg.decode('utf-8')=='bye':break inp = input('>>>') udpsk.sendto(bytes(inp,encoding='utf-8'),addr) #发送消息 udpsk.close() #关闭套接字
数据传输过程中的黏包现象
现象:
接受和发送不匹配
基于UDP协议的传输不会出现黏包现象,一次接收不完的信息会被丢掉
基于TCP协议的传输会出现黏包现象,但是不丢包
基于UDP协议的传输会限制数据包的大小,超过的信息被丢掉
基于TCP协议的传输不会限制数据包的大小
产生黏包的原因:
TCP协议是面向流的,无边界的
TCP协议有一种拆包机制,当发送的数据包大于网关的mtu(网络上传输的最大数据包),该数据包就会被拆开,分批发送
Nagle算法:优化传输速度(将多次间隔较小且数据量小的数据合并成一个大的数据块,封包)导致黏包现象
黏包出现的最终原因:不知道要接受的数据有多大
解决黏包方式1
先发送数据包的长度,根据长度接受数据(缺点:多了一次客户服务器之间的交互)
import socket sock = socket.socket() sock.bind(('127.0.0.1',9991)) sock.listen() conn,addr = sock.accept() while 1: cmd = input('>>>') conn.send(cmd.encode('gbk')) len1 = conn.recv(1024) #先接受长度 recv = conn.recv(int(len1.decode('gbk'))) #根据长度接受数据包 print('std:'+recv.decode('gbk'))
import socket import subprocess sock = socket.socket() sock.connect(('127.0.0.1',9991)) while 1: cmd = sock.recv(1024) #执行计算机命令subprocess sb = subprocess.Popen(cmd.decode('gbk'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout = sb.stdout.read() stderr = sb.stderr.read() print('stdout'+str(len(stdout))) print('stderr' + str(len(stderr))) sock.send(str(len(stdout)+len(stderr)).encode('gbk')) #发送长度 sock.send(stdout) #发送消息 sock.send(stderr)
借助struct模块解决黏包
struct模块
import struct p = struct.pack('i',20)#int转bytes print(p) num = b'1223' unp = struct.unpack('i',num)[0]#bytes转int print(unp)
socketServer
import socketserver class Myserver(socketserver.BaseRequestHandler): #继承BaseRequestHandler def handle(self): #重写handle函数 self.data = self.request.recv(1024) #self.request相当与conn,接受信息 print(self.client_address[0]) print(self.data.decode('utf-8')) self.request.sendall(self.data) #发送消息 if __name__ == '__main__': host,port = '127.0.0.1',9999 socketserver.TCPServer.allow_reuse_address = True # 设置allow_reuse_address允许服务器重用地址 server = socketserver.TCPServer((host,port),Myserver) # 创建一个server, 将服务地址绑定到127.0.0.1:9999 server.serve_forever() #程序一直运行