socketpython文档_python基础(socket)

socketpython文档_python基础(socket)

2023年7月15日发(作者:)

socketpython⽂档_python基础(socket)⼀、软件开发的架构:1.1 C/S架构:C/S即:Client与Server ,中⽂意思:客户端与服务器端架构,这种架构也是从⽤户层⾯(也可以是物理层⾯)来划分的。这⾥的客户端⼀般泛指客户端应⽤程序EXE,程序需要先安装后,才能运⾏在⽤户的电脑上,对⽤户的电脑操作系统环境依赖较⼤。1.2 B/S架构:B/S即:Browser与Server,中⽂意思:浏览器端与服务器端架构,这种架构是从⽤户层⾯来划分的。Browser浏览器,其实也是⼀种Client客户端,只是这个客户端不需要⼤家去安装什么应⽤程序,只需在浏览器上通过HTTP请求服务器端相关的资源(⽹页资源),客户端Browser浏览器就能进⾏增删改查⼆、 socket概念:2.1 定义:Socket是应⽤层与TCP/IP协议族通信的中间软件抽象层,它是⼀组接⼝。在设计模式中,Socket其实就是⼀个门⾯模式,它把复杂的TCP/IP协议族隐藏在Socket接⼝后⾯,对⽤户来说,⼀组简单的接⼝就是全部,让Socket去组织数据,以符合指定的协议。2.2 套接字:2.2.1 基于⽂件类型的套接字家族:套接字家族的名字:AF_UNIXunix⼀切皆⽂件,基于⽂件的套接字调⽤的就是底层的⽂件系统来取数据,两个套接字进程运⾏在同⼀机器,可以通过访问同⼀个⽂件系统间接完成通信2.2.2 基于⽹络类型的套接字家族:套接字家族的名字:AF_INET(还有AF_INET6被⽤于ipv6,还有⼀些其他的地址家族,不过,他们要么是只⽤于某个平台,要么就是已经被废弃,或者是很少被使⽤,或者是根本没有实现,所有地址家族中,AF_INET是使⽤最⼴泛的⼀个,python⽀持很多种地址家族,但是由于我们只关⼼⽹络编程,所以⼤部分时候我么只使⽤AF_INET)2.3 tcp与udp的应⽤:2.3.1 基于TCP协议的socket:tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端⽰例:#通⽤测试版(服务端):importsocketsk= () #socket对象,实际上存储了存储了所有的操作系统提供的⽹络资源(('127.0.0.1', 9006)) #只有我⾃⼰能访问我⾃⼰#(('192.168.16.179', 9006)) # 局域⽹类的所有的机器都可以访问我,但是如果这个代码移交给别的机器必须修改成别的机器的ip地址#(('0.0.0.0', 9006)) # 所有访问我这台机器的通信ip地址(192.168.12.11),都能够访问到我的服务() #监听(可以设置接收客户端等待的个数)conn, addr= () #阻塞,连接对象:存储本地的ip,port和对⽅的ip、port等print(conn) #像⼀个联通的通道,记录server的地址,client的地址等print(addr) #client的地址('我是服务器!'.encode('utf-8')) #编码发送内容msg = (1024) #获取1024个字节内容print(('utf-8'), addr) #解码接收的内容()#关闭连接() #关闭server服务#通⽤测试版(客户端):importsocketsk= () #创建socket对象t(('127.0.0.1', 9006)) #连接指定服务器 IP地址 + portcontent= (1024).decode('utf-8')print(content)msg= input('请输⼊⼀个内容:')(('utf-8')) #发送⼀个内容到服务器()#断开与服务端的连接通⽤测试版(#⼀个服务端与多个客户端通信(服务端):importsocketsk=()(('127.0.0.1', 9005))()whileTrue:#等待⼀个客户端的连接conn, addr = () #内容 + 地址whileTrue:msg= input('请输⼊⼀个内容:')(('utf-8'))if () == 'Q': #退出当前与客户端的连接breakcontent= (1024).decode('utf-8')print(content)if () == 'Q': #接收到⼀个客户端的请求退出break#关闭当前连接()()#⼀个服务端与多个客户端通信(客户端):importsocketsk=()t(('127.0.0.1', 9005))whileTrue:content= (1024).decode('utf-8')print(content)if () == 'Q': #接收到⼀个服务端的请求退出breakmsg= input('请输⼊⼀个内容:')(('utf-8'))if () == 'Q': #退出当前与服务端的连接()⼀个服务端与多个客户端通信2.3.2 基于UDP协议的socket:udp是⽆链接的,启动服务之后可以直接接受消息,不需要提前建⽴链接⽰例:## 通⽤测试版(服务端):importsocketsk= (type=_DGRAM) #创建⼀个udp的套接字对象(('127.0.0.1', 9002)) #绑定 本地回环地址 + 端⼝msg, client_addr= om(1024) #接收⼀个内容 内容本⾝ + 客户端的地址print(('utf-8')) #打印接收到的客户端信息print(client_addr) #客户端地址('收到'.encode('utf-8'), client_addr) #发送⼀个信息到客户端() #关闭当前与客户端的连接#通⽤测试版(客户端):importsocketsk= (type=_DGRAM) #SOCK_DGRAM udp服务(默认是tcp服务,不需要指定type)server_addr= ('127.0.0.1', 9002) #指定需要连接的服务端地址 + 端⼝('你好'.encode('utf-8'), server_addr) #指定发送内容 + server_addrmsg, _= om(1024) #接收来⾃服务端的信息 内容本⾝ + 服务端的地址print(('utf-8'))#msg = (1024).decode('utf-8') # 只接收服务端的信息本⾝,不接收地址⽤recv#print(msg)()#关闭当前与服务端的连接通⽤测试版#⼀个服务端与多个客户端通信(服务端):importsocketsk= (type=_DGRAM)(('127.0.0.1', 9002))whileTrue:msg, client_addr= om(1024)print(('utf-8'))info= input('请输⼊⼀个内容:')(('utf-8'), client_addr)()#⼀个服务端与多个客户端通信(客户端):importsocketsk= (type=_DGRAM)server_addr= ('127.0.0.1', 9002)whileTrue:info= input('请输⼊⼀个内容:')if () == 'Q': break #客户端⾃动断开与服务端的连接,服务端不需要接收消息,直接(('utf-8'), server_addr)msg, _= om(1024) #通常⽤_表⽰不使⽤这个变量(占位变量)print(('utf-8'))if ('utf-8').upper() == 'Q': #接收到⼀个服务端的断开请求()⼀个服务端与多个客户端通信2.3.3 socket参数的详解:创建socket对象的参数说明:family地址系列应为AF_INET(默认值),AF_INET6,AF_UNIX,AF_CAN或AF_RDS。(AF_UNIX 域实际上是使⽤本地 socket ⽂件来通信)type套接字类型应为SOCK_STREAM(默认值),SOCK_DGRAM,SOCK_RAW或其他SOCK_常量之⼀。SOCK_STREAM 是基于TCP的,有保障的(即能保证数据正确传送到对⽅)⾯向连接的SOCKET,多⽤于资料传送。SOCK_DGRAM 是基于UDP的,⽆保障的⾯向消息的socket,多⽤于在⽹络上发⼴播信息。proto协议号通常为零,可以省略,或者在地址族为AF_CAN的情况下,协议应为CAN_RAW或CAN_BCM之⼀。fileno如果指定了fileno,则其他参数将被忽略,导致带有指定⽂件描述符的套接字返回。与()不同,fileno将返回相同的套接字,⽽不是重复的。这可能有助于使⽤()关闭⼀个独⽴的插座。三、黏包:3.1 黏包现象:同时执⾏多条命令之后,得到的结果很可能只有⼀部分,在执⾏其他命令的时候⼜接收到之前执⾏的另外⼀部分结果,这种显现就是黏包。注意:只有TCP有粘包现象,UDP永远不会粘包3.2 TCP协议中的数据传递:3.2.1 tcp协议的拆包机制:当发送端缓冲区的长度⼤于⽹卡的MTU时,tcp会将这次发送的数据拆成⼏个数据包发送出去。MTU是Maximum Transmission Unit的缩写。意思是⽹络上传送的最⼤数据包。MTU的单位是字节。 ⼤部分⽹络设备的MTU都是1500。如果本机的MTU⽐⽹关的MTU⼤,⼤的数据包就会被拆开来传送,这样会产⽣很多数据包碎⽚,增加丢包率,降低⽹络速度。View Code3.2.2 基于tcp协议特点的黏包现象成因:在发送端 由于两条消息发送的间隔时间很短,且两条消息本⾝也很短,在发送之前被合成⼀条消息(合包机制)在接收端 由于接收不及时导致两条先后到达的信息在接收端黏在了⼀起黏包的本质:信息与信息之间没有边界的流式传输3.3 UDP不会发⽣黏包:UDP(user datagram protocol,⽤户数据报协议)是⽆连接的,⾯向消息的,提供⾼效率服务。不会使⽤块的合并优化算法,, 由于UDP⽀持的是⼀对多的模式,所以接收端的skbuff(套接字缓冲区)采⽤了链式结构来记录每⼀个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端⼝等信息),这样,对于接收端来说,就容易进⾏区分处理了。 即⾯向消息的通信是有消息保护边界的。对于空消息:tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防⽌程序卡住,⽽udp是基于数据报的,即便是你输⼊的是空内容(直接回车),也可以被发送,udp协议会帮你封装上消息头发送过去。不可靠不黏包的udp协议:udp的recvfrom是阻塞的,⼀个recvfrom(x)必须对唯⼀⼀个sendinto(y),收完了x个字节的数据就算完成,若是y;x数据就丢失,这意味着udp根本不会粘包,但是会丢数据,不可靠。View Code⽤UDP协议发送时,⽤sendto函数最⼤能发送数据的长度为:65535- IP头(20) – UDP头(8)=65507字节。⽤sendto函数发送数据时,如果发送数据长度⼤于该值,则函数会返回错误。(丢弃这个包,不进⾏发送)⽤TCP协议发送时,由于TCP是数据流协议,因此不存在包⼤⼩的限制(暂不考虑缓冲区的⼤⼩),这是指在⽤send函数时,数据长度参数不受限制。⽽实际上,所指定的这段数据并不⼀定会⼀次性发送出去,如果这段数据⽐较长,会被分段发送,如果⽐较短,可能会等待和下⼀次数据⼀起发送。总结:黏包现象只发⽣在tcp协议中:1.从表⾯上看,黏包问题主要是因为发送⽅和接收⽅的缓存机制、tcp协议⾯向流通信的特点。2.实际上,主要还是因为接收⽅不知道消息之间的界限,不知道⼀次性提取多少字节的数据所造成的3.4 黏包的解决⽅案:⾃定义协议:struct模块特点:该模块可以把⼀个类型,如数字,转成固定长度的bytes>>> ('i',11):'i' format requires -2147483648 <= number <= 2147483647 #这个是范围View Code3.4.1 使⽤struct模块的解决⽅案⼀(不推荐使⽤):借助struct模块,我们知道长度数字可以被转换成⼀个标准⼤⼩的4字节数字。因此可以利⽤这个特点来预先发送数据长度发送时接收时先发送struct转换好的数据长度4字节先接受4个字节使⽤struct转换成数字来获取要接收的数据长度再发送数据再按照长度接收数据#每次⽂件的上传与下载都需要发送数据长度,浪费系统资源,⼀般采⽤⽅案⼆的做法,把所有⽂件⼤⼩写在#⼀个字典中,最后通过发送字典得到数据长度来获取每次的内容⼤⼩#服务器:importstructimportsocketsk=()(('127.0.0.1', 9001))()conn, addr=()num= (4)num= ('i', num)[0] #获得⽂件名的长度file_name = (num).decode('utf-8') #根据长度获取⽂件名filesize = (4)filesize= ('i', filesize)[0] #获取⽂件长度with open(file_name, 'wb') as f:content= (filesize) #根据⽂件长度获取⽂件内容(content)()()服务器#客户端:importosimportstructimportsocketsk=()t(('127.0.0.1', 9001))filepath= input('请输⼊⽂件路径 :')filename= me(filepath).encode('utf-8') #获取⽂件名的bytesname_len = ('i', len(filename)) #数据长度(name_len) #先发送⼀个数据长度(filename) #发送⽂件名的真实内容filesize = e(filepath) #⽂件⼤⼩file_len = ('i', filesize) #⽂件长度(file_len) #发送⽂件长度#读取⽂件,发送⽂件内容with open(filepath, 'rb') as f:content=()(content)()客户端3.4.2 使⽤struct模块的解决⽅案⼆:我们还可以把报头做成字典,字典⾥包含将要发送的真实数据的详细信息,然后json序列化,然后⽤struck将序列化后的数据长度打包成4个字节(4个⾃⼰⾜够⽤了)发送时接收时先发报头长度先收报头长度,⽤struct取出来再编码报头内容然后发送根据取出的长度收取报头内容,然后解码,反序列化最后发真实内容从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容#服务器:importjsonimportstructimportsocketsk=()(('127.0.0.1', 9001))()conn, addr=()num= (4) #接收数据报头信息num = ('i', num)[0] #struct解包后得到⼀个元组str_dic = (num).decode('utf-8') #接收指定⽂件⼤⼩dic = (str_dic) #字符串转化成字典with open(dic['filename'], 'wb') as f:content= (dic['filesize']) #接收这个⽂件(content)()()服务器#客户端:importosimportjsonimportstructimportsocketsk=()t(('127.0.0.1',9001))filepath= input('请输⼊⽂件路径 :')filename= me(filepath) #⽂件名filesize = e(filepath) #⽂件⼤⼩dic = {'filename': filename, 'filesize':filesize} #创建⼀个字典str_dic = (dic) #序列化成字符串bytes_dic = str_('utf-8')len_dic= len(bytes_dic) #获取字典中字节长度bytes_len = ('i', len_dic) #struct字节的长度(bytes_len) #发送报头(bytes_dic) #发送内容字节with open(filepath, 'rb') as f:content=()(content)()客户端四、 socketserver服务:tcp协议能够处理多个client的请求,实现并发请求:⽰例:#⽰例:#服务端importsocketserverimporttimeclass Myserver(questHandler): #创建⼀个服务器类⽅法def handle(self): #重写handle这个⽅法conn = t #获取⼀个连接(ip + prot)for i in range(200):(('hello % i' % i).encode('utf-8'))print((1024))(0.5)server= ingTCPServer(('127.0.0.1', 9005), Myserver) #实例化⼀个_forever() #等待连接服务端#客户端:importsocketsk=()t(('127.0.0.1', 9005))whileTrue:msg= (1024)print(('utf-8'))(b'byebye')()客户端五、 hmac模块应⽤:5.1 定义:以⼀个密钥和⼀个消息为输⼊,⽣成⼀个消息摘要作为输出通过两个bytes类型的字符串得到⼀个新的bytes类型的字节码5.2 hmac与hashlib⽐较:hmac:⽣成⼀个bytes类型的字节码hashlib:⽣成的是⼀个字符串⽰例:#⽰例:#hmac消息摘要算法:importosimporthmacprint(m(32)) #⽣成⼀个32位的字节码hmac = (b'alex sb', m(32))print(()) #加密后的字节码View Code六、 验证消息合法性:6.1 tcp客服端与服务器的消息合法性验证:定义:服务器读取⽤户数据库中的⽤户密码与发送的随机值做与客户端⼀样的hmac运算,然后与⽤户发送的结果⽐较,如果结果⼀致则验证⽤户合法⽰例:#⽰例:#服务器:importosimporthmacimportsocketsecret_key= b'alex sb'#m(32) 给每⼀客户端发送⼀个随机的字符串,来保证即使数据被拦截你也不能使⽤这个消息sk =()(('127.0.0.1', 9001))()conn, addr=()rand= m(32)(rand)hmac= (secret_key, rand) #hmac摘要算法res =()ret= (1024)if ret ==res:print('是合法的客户端')else:print('不是合法的客户端')()服务端#客户端:importhmacimportsocketsecret_key= b'alex sb'sk=()t(('127.0.0.1', 9001))rand= (32)hmac=(secret_key, rand)res=()(res)()客户端6.2 端⼝占⽤处理的处理⽅式:定义:防⽌程序因为意外退出之后重启程序出现端⼝被占⽤的错⽣产环境中不能⽤,平时开发过程中可以设置⽰例:#⽰例:importsocketsk=()kopt(_SOCKET, _REUSEADDR,1) #重置端⼝,归还系统资源(('127.0.0.1', 9001))()#其他代码...()View Code七、 ⾮阻塞模型:#⽰例:#⾮阻塞实现多⽤户请求:importsocketsk=()(('127.0.0.1', 9001))cking(False)#blocking阻塞 setblocking设置阻塞,默认为阻塞态()conn_l= [] #⽤户列表whileTrue:try:conn, addr=()conn_(conn)exceptBlockingIOError:for conn inconn_l:try:msg= (1024).decode('utf-8')print(msg)(().encode('utf-8'))exceptBlockingIOError:passView Code

发布者:admin,转转请注明出处:http://www.yc00.com/web/1689409059a243376.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信