网络编程的一些基本概念:
1.地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议。
互联网协议与osi模型
互联网协议按照功能不同分为osi七层或tcp/ip五层或tcp/ip四层
每层运行常见物理设备
一.套接字(socket)初使用
基于TCP协议的socket
tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
服务端 import socket s = socket.socket() #买电话 id_port = ('127.0.0.1',8000) #买电话卡 s.bind(id_port) #把电话装上电话卡
s.listen() #等待电话打进来 conn,adress = s.accept() #接受到了消息 msg = conn.recv(1024) #接受信息,接受的一点是字节类型 print(msg.decode()) # 打印,解码 inp = input('<<<<') #输入要发送的信息 new_msg = conn.send(inp.encode('utf-8)) # 把发送的信息编码,发送 s.close()
客户端 import socket s = socket.socket() id_port = ('127.0.0.1',8000) s.connect(id_port) msg = input('<<<') s.send(msg.encode('utf-8') new_msg = s.recv(1024) print(new_msg.decode())
s.close()
基于UDP协议的socket
udp是无链接的,启动服务之后可以直接接受消息,不需要提前建立链接
简单使用
服务端 import socket udp_s = socket.socket(type = socket.SOCK_DGRAM)# 创建套接字 id_port = ('127.0.01',8000) udp_s.bind(id_port) #为套接字绑定ip msg,addr = udp_s.recvfrom(1024) print(msg.decode()) inp = input('<<<') udp_s.sendto(inp.encode('utf-8),addr) #这里发送消息给客户端要带上地址, udp_s.close()
客户端 import socket s = socket.socket(type = socket.SOCK_DRGAM) id_port = ('127.0.0.1',8000) msg = input('<<<') s.sendto(msg.encode('utf-8'),idport) msg,addr = s.recv(1024) print(msg.decode('utf-8))
s.close()
udp协议和tcp的差别:
服务端:tcp的服务端需要s.listen()这个过程 并且是conn,addr = s.accept() 后面的send 和recv 都是通过conn来进行,所以后面的发送不需要把addr加上。
udp的服务端没有listen这个过程,直接是msg,addr = s.recv(1024) 后面的是s.sendto(mag,addr)
客户端:tcp的客户端是id_port 与s绑定是s.connect(id_port) s.send() msgr = s.recv(1024)
udp的客户端是不需要客户端与ip_port 绑定的的,直接发送的时候 s.sendto(msg,id_port) msg,addr = s.recv(1024)
二、黏包问题
tcp协议的黏包成因详谈:
首先明确一点,黏包只发生在TCP协议,udp协议不会产生黏包,udp要么报错,要么发送丢失,也就是不完整。至于为什么我们稍后来谈。这里只搞清楚一点只有tcp会有黏包现象。借助struct模块,我们知道长度数字可以被转换成一个标准大小的4字节数字。因此可以利用这个特点来预先发送数据长度。
发送时 | 接收时 |
先发送struct转换好的数据长度4字节 | 先接受4个字节使用struct转换成数字来获取要接收的数据长度 |
再发送数据 | 再按照长度接收数据 |
server端 #!user/bin/python3 #Author:Mr.Yuan #-*- coding:utf-8 -*- #@time: 2018/5/7 19:00 import socket import os import struct import json import time id_port = ('127.0.0.1',9000) s = socket.socket() s.bind(id_port) s.listen() cnng,addr = s.accept() dic = {'filename':r'H:\pycharm文件\a', 'filesize':os.path.getsize(r'D:\feiq\Recv Files\python11期day35\video2.mp4')} str_dic = json.dumps(dic).encode('utf-8') struct_dic = struct.pack('i',len(str_dic)) cnng.send(struct_dic) cnng.send(str_dic) f = open(r'D:\feiq\Recv Files\python11期day35\video2.mp4','rb') while dic['filesize']: content = f.read(1024) dic['filesize']-=len(content) print(dic['filesize']) cnng.sendall(content) cnng.close() s.close()
client端 #!user/bin/python3 #Author:Mr.Yuan #-*- coding:utf-8 -*- #@time: 2018/5/7 19:00 import socket import json import struct id_port = ('127.0.0.1',9000) s = socket.socket() s.connect(id_port) struct_message = s.recv(4) dic_len = struct.unpack('i',struct_message)[0] str_dic = json.loads(s.recv(dic_len).decode()) print(str_dic) with open('H:\pycharm文件\mp6.mp4','wb') as f : while str_dic['filesize']: recv_content = s.recv(1024) str_dic['filesize'] -= len(recv_content) print(str_dic['filesize']) f.write(recv_content) s.close()
四、一个服务端连接多个客户端写法
#服务端 import socketserver class MyServe(socketserver.BaseRequestHandler): def handle(self):#必须写这个函数名 最先执行这个方法 self.request.sendall(bytes('欢迎致电10086...巴拉巴拉一大推',encoding='utf-8')) while True: date = self.request.recv(1024) print('%s:%s'% (self.client_address,date.decode())) self.request.sendall(bytes('我收到了',encoding='utf-8')) if __name__ == '__main__': server = socketserver.ThreadingTCPServer(('127.0.0.1',8000),MyServe) server.serve_forever() #让handle方法永远执行下去
#客户端 import socket ip_port = ('127.0.0.1',8000) s = socket.socket() s.connect(ip_port) msg = s.recv(1024) print(msg.decode()) while True: inp = input('<<<<') if len(inp)==0:continue s.send(bytes(inp,encoding='utf-8')) data = s.recv(1024) print(data.decode()) s.close()