网络编程知识点剖析
网络编程知识点剖析
一. C/S 架构: Client / Server 客户端 / 服务端
B/S 架构: Browser / Server 前端 / 服务端
二.网络编程通信流程.
网卡 mac地址 IP地址 子网掩码 网关 DNS服务器 (进行域名(domain name)和与之相对应的IP地址 (IP address)转换的服务器。)
DHCP (自动分配IP) NAT (Network Address Translation,网络地址转换) 端口 路由器
交换机 集线器 广播 单播 广播风暴 arp协议(地址解析协议) 路由协议
三.网络通信协议
1. osi七层: tcp\ ip 五层:
应用层 应用层
表示层 传输层
会话层 网路层
传输层 数据链路层
网络层 物理层
数据链路层
物理层
2. TCP\IP 协议存在 传输层
Tcp : 面向连接,消息可靠,效率相对差,面向流的消息格式,无消息保护边界
Udp : 面向无连接,消息不可靠,效率高,面向包的消息格式,有消息保护边界
tcp三次握手:1.(client) > [SYN包 ]->(server) 请求建立连接
2.(client) < [SYN/ACK] < (server) severs收到syn 发送[SYN/ACK]确认包
3.(client)> [ACK] >(server) client收到[SYN/ACK] 在发一个[ACK]确认包
tcp四次挥手:1.(client) > [ACK/FIN] >(server) 发送包 请求关闭
2.(client) < [ACK] <(server) 收到包 同意关闭
3.(client) <[ACK/FIN] <(server) 收到包 是否收到 同意关闭 消息
4.(client) > [ACK/FIN] >(server) 发送包 收到过程2的消息,
四.socket
1. 服务端与客户端 手法消息 基本结构
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
server>>>>>>>>>>>>>>>>> import socket sk = socket.socket() sk.bind(('127.0.0.1',8898)) #把地址绑定到套接字 sk.listen() #监听链接 conn,addr = sk.accept() #接受客户端链接 ret = conn.recv(1024) #接收客户端信息 print(ret) #打印客户端信息 conn.send(b'hi') #向客户端发送信息 conn.close() #关闭客户端套接字 sk.close() #关闭服务器套接字(可选) client>>>>>>>>>>>>>>>> import socket sk = socket.socket() # 创建客户套接字 sk.connect(('127.0.0.1',8898)) # 尝试连接服务器 sk.send(b'hello!') ret = sk.recv(1024) # 对话(发送/接收) print(ret) sk.close() # 关闭客户套接字
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
server>>>>>>>>>>>>>>>>>>>>>>>> import socket udp_sk = socket.socket(type=socket.SOCK_DGRAM) #创建一个服务器的套接字 udp_sk.bind(('127.0.0.1',9000)) #绑定服务器套接字 msg,addr = udp_sk.recvfrom(1024) print(msg) udp_sk.sendto(b'hi',addr) # 对话(接收与发送) udp_sk.close() # 关闭服务器套接字 client>>>>>>>>>>>>>>>>>>>>>>>> import socket ip_port=('127.0.0.1',9000) udp_sk=socket.socket(type=socket.SOCK_DGRAM) udp_sk.sendto(b'hello',ip_port) back_msg,addr=udp_sk.recvfrom(1024) print(back_msg.decode('utf-8'),addr)
2.缓冲区
作用: 提高效率,将发送或者接受信息的过程与网络传输隔离开,解耦
特性: 1.I/O缓冲区在每个TCP套接字中单独存在;
2.I/O缓冲区在创建套接字时自动生成;
3.即使关闭套接字也会继续传送输出缓冲区中遗留的数据;
4.关闭套接字将丢失输入缓冲区中的数据。
大小: 一般默认为 8K, 可以通过 getsockopt() 函数获取
3.粘包
TCP会粘包、UDP永远不会粘包
粘包的两种现象:
1.连续发送小包并且间隔时间很短,就会发送两个消息合并在一起的情况,被一次接受了,(Nagel 优化算法导致的, 避免连续发送小包,影响传输xiaol)
2.一次发送数据过大,对方一次未接受完导致下次接受的时候,连同第一次剩下的消息,一同接受了,导致粘包.
解决粘包的方案:
原因是因为双方不知道对方发送的消息长度
1.发送消息之前先发送消息的长度,然后收到对方的确认信息后再发送消息.
2.通过struct模块,自定义包头,将消息长度打包成4个字节长度的信息,连同你要发送的数据,一起发送过去
打包:pack('i',长度) 长度是个整数
解包:unpack('i',接受的那4个字节),得到的是元祖
Sendall(): 循环的send直到数据发送完毕.
Socketsserver(): 实现tcp协议下,一个服务端可以同时和多个客户端进行通信.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket,struct,json import subprocess 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) while True: conn,addr=phone.accept() while True: cmd=conn.recv(1024) if not cmd:break print('cmd: %s' %cmd) res=subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) err=res.stderr.read() print(err) if err: back_msg=err else: back_msg=res.stdout.read() headers={'data_size':len(back_msg)} head_json=json.dumps(headers) head_json_bytes=bytes(head_json,encoding='utf-8') conn.send(struct.pack('i',len(head_json_bytes))) #先发报头的长度 conn.send(head_json_bytes) #再发报头 conn.sendall(back_msg) #在发真实的内容 conn.close()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket import struct,json client= socket.socket() client.connect(("127.0.0.1",8080)) while True: cmd=input(">>: ") if not cmd:continue client.send(bytes(cmd,encoding='utf-8')) head=client.recv(4) head_json_len=struct.unpack('i',head)[0] head_json=json.loads(client.recv(head_json_len).decode('utf-8')) data_len=head_json['data_size'] recv_size=0 recv_data=b'' while recv_size < data_len: recv_data = client.recv(1024) + recv_data recv_size = len(recv_data) + recv_size print(recv_data.decode('gbk'))
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socketserver class Myserver(socketserver.BaseRequestHandler): def handle(self): self.data = self.request.recv(1024).strip() print("{} wrote:".format(self.client_address[0])) print(self.data) self.request.sendall(self.data.upper()) if __name__ == "__main__": HOST, PORT = "127.0.0.1", 9999 # 设置allow_reuse_address允许服务器重用地址 socketserver.TCPServer.allow_reuse_address = True # 创建一个server, 将服务地址绑定到127.0.0.1:9999 #server = socketserver.TCPServer((HOST, PORT),Myserver) server = socketserver.ThreadingTCPServer((HOST, PORT),Myserver) # 让server永远运行下去,除非强制停止程序 server.serve_forever()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket HOST, PORT = "127.0.0.1", 9999 data = "hello" # 创建一个socket链接,SOCK_STREAM代表使用TCP协议 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.connect((HOST, PORT)) # 链接到客户端 sock.sendall(bytes(data + "\n", "utf-8")) # 向服务端发送数据 received = str(sock.recv(1024), "utf-8")# 从服务端接收数据 print("Sent: {}".format(data)) print("Received: {}".format(received))