楔子python
你如今已经学会了写python代码,假如你写了两个python文件a.py和b.py,分别去运行,你就会发现,这两个python的文件分别运行的很好。可是若是这两个程序之间想要传递一个数据,你要怎么作呢?web
这个问题以你如今的知识就能够解决了,咱们能够建立一个文件,把a.py想要传递的内容写到文件中,而后b.py从这个文件中读取内容就能够了。shell
可是当你的a.py和b.py分别在不一样电脑上的时候,你要怎么办呢?编程
相似的机制有计算机网盘,qq等等。咱们能够在咱们的电脑上和别人聊天,能够在本身的电脑上向网盘中上传、下载内容。这些都是两个程序在通讯。windows
软件开发的架构设计模式
咱们了解的涉及到两个程序之间通信的应用大体能够分为两种:浏览器
第一种是应用类:qq、微信、网盘、优酷这一类是属于须要安装的桌面应用缓存
第二种是web类:好比百度、知乎、博客园等使用浏览器访问就能够直接使用的应用服务器
这些应用的本质其实都是两个程序之间的通信。而这两个分类又对应了两个软件开发的架构~微信
1,C/S架构
C/S即:Client与Server ,中文意思:客户端与服务器端架构,这种架构也是从用户层面(也能够是物理层面)来划分的。
这里的客户端通常泛指客户端应用程序EXE,程序须要先安装后,才能运行在用户的电脑上,对用户的电脑操做系统环境依赖较大。
2,B/S架构
B/S即:Browser与Server,中文意思:浏览器端与服务器端架构,这种架构是从用户层面来划分的。
Browser浏览器,其实也是一种Client客户端,只是这个客户端不须要你们去安装什么应用程序,只需在浏览器上经过HTTP请求服务器端相关的资源(网页资源),客户端Browser浏览器就能进行增删改查。
网络基础
1,计算机网络的发展及基础网络概念
1.1,早期 : 联机
1.2,以太网 : 局域网与交换机
2,交换机 —— 解决 多台机器以前的通讯问题
3,广播
主机之间“一对全部”的通信模式,网络对其中每一台主机发出的信号都进行无条件复制并转发,全部主机均可以接收到全部信息(无论你是否须要),因为其不用路径选择,因此其网络成本能够很低廉。有线电视网就是典型的广播型网络,咱们的电视机其实是接受到全部频道的信号,但只将一个频道的信号还原成画面。在数据网络中也容许广播的存在,但其被限制在二层交换机的局域网范围内,禁止广播数据穿过路由器,防止广播数据影响大面积的主机。
4,ip地址与ip协议
规定网络地址的协议叫ip协议,它定义的地址称之为ip地址,普遍采用的v4版本即ipv4,它规定网络地址由32位2进制表示
3.1,ipv4
4个点分十进制(4个8位2进制数):00000000.00000000.00000000.00000000
范围 0.0.0.0——255.255.255.255
保留字段 192.168.----
127.0.0.1 本地的回环地址
3.2,ipv6
6个点分十进制(6个8位2进制数):
范围 0.0.0.0.0.0 - 255.255.255.255.255.255
5,mac地址
head中包含的源和目标地址由来:ethernet规定接入internet的设备都必须具有网卡,发送端和接收端的地址即是指网卡的地址,即mac地址。
mac地址:每块网卡出厂时都被烧制上一个世界惟一的mac地址,长度为48位2进制,一般由12位16进制数表示(前六位是厂商编号,后六位是流水线号)
如上图,一个圆圈内是一个局域网
局域网(Local Area Network,LAN)是指在某一区域内由多台计算机互联成的计算机组。通常是方圆几公里之内。局域网能够实现文件管理、应用软件共享、打印机共享、工做组内的日程安排、电子邮件和传真通讯服务等功能。局域网是封闭型的,能够由办公室内的两台计算机组成,也能够由一个公司内的上千台计算机组成。
所谓”子网掩码”,就是表示子网络特征的一个参数。它在形式上等同于IP地址,也是一个32位二进制数字,它的网络部分所有为1,主机部分所有为0。好比,IP地址172.16.10.1,若是已知网络部分是前24位,主机部分是后8位,那么子网络掩码就是11111111.11111111.11111111.00000000,写成十进制就是255.255.255.0。
知道”子网掩码”,咱们就能判断,任意两个IP地址是否处在同一个子网络。方法是将两个IP地址与子网掩码分别进行AND运算(两个数位都为1,运算结果为1,不然为0),而后比较结果是否相同,若是是的话,就代表它们在同一个子网络中,不然就不是。
好比,已知IP地址172.16.10.1和172.16.10.2的子网掩码都是255.255.255.0,请问它们是否在同一个子网络?二者与子网掩码分别进行AND运算, 172.16.10.1:10101100.00010000.00001010.000000001 255255.255.255.0:11111111.11111111.11111111.00000000 AND运算得网络地址结果:10101100.00010000.00001010.000000001->172.16.10.0 172.16.10.2:10101100.00010000.00001010.00000010 255255.255.255.0:11111111.11111111.11111111.00000000 AND运算得网络地址结果:10101100.00010000.00001010.000000001->172.16.10.0 结果都是172.16.10.0,所以它们在同一个子网络。
4,网段
# 交换机 —— 多台机器以前的通讯问题
# 网关的概念 —— 局域网中的机器想要访问局域网外的机器,须要经过网关访问
# 网段—— ip地址 和 子网掩码 按位与 获得网段地址,即ip地址前三个数。
例如:
# 255.255.255.0 子网掩码
#192.168.13.253 ip地址的网段为:192.168.13.0
192.168.13.0 到 192.168.13.255 为一段
5,端口
6,一个程序如何在网络上找到另外一个程序?
计算机网络协议
互联网的核心就是由一堆协议组成,协议就是标准,好比全世界人通讯的标准是英语,若是把计算机比做人,互联网协议就是计算机界的英语。全部的计算机都学会了互联网协议,那全部的计算机都就能够按照统一的标准去收发信息从而完成通讯了。
1,osi七层模型
1.1,互联网协议按照功能不一样分为 osi七层 或 tcp/ip五层 或 tcp/ip四层
1.2,每层运行常见物理设备
1.3,每层运行常见的协议
2,socket层 概念
2.1,理解socket
Socket是应用层与TCP/IP协议族通讯的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来讲,一组简单的接口就是所有,让Socket去组织数据,以符合指定的协议。
2.2,站在你的角度上看socket
其实站在你的角度上看,socket就是一个模块。咱们经过调用模块中已经实现的方法创建两个进程之间的链接和通讯。
也有人将socket说成ip+port,由于ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序。
因此咱们只要确立了ip和port就能找到一个应用程序,而且使用socket模块来与之通讯。
2.3,socket层
3,套接字(socket)的发展史
套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 所以,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通信。这也被称进程间通信,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。
3.1,基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,能够经过访问同一个文件系统间接完成通讯
3.2,基于网络类型的套接字家族
套接字家族的名字:AF_INET
(还有AF_INET6被用于ipv6,还有一些其余的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是不多被使用,或者是根本没有实现,全部地址家族中,AF_INET是使用最普遍的一个,python支持不少种地址家族,可是因为咱们只关心网络编程,因此大部分时候我么只使用AF_INET)
TCP(Transmission Control Protocol)可靠的、面向链接的协议(eg:打电话)、传输效率低、全双工通讯(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。
UDP(User Datagram Protocol)不可靠的、无链接的服务,传输效率高(发送前时延小),一对1、一对多、多对1、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。
TCP---传输控制协议,提供的是面向链接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间创建一个TCP链接,以后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另外一端。
UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,可是并不能保证它们能到达目的地。因为UDP在传输数据报前不用在客户和服务器之间创建一个链接,且没有超时重发等机制,故而传输速度很快
如今Internet上流行的协议是TCP/IP协议,该协议中对低于1024的端口都有确切的定义,他们对应着Internet上一些常见的服务。这些常见的服务能够分为使用TCP端口(面向链接)和使用UDP端口(面向无链接)两种。 说到TCP和UDP,首先要明白“链接”和“无链接”的含义,他们的关系能够用一个形象地比喻来讲明,就是打电话和写信。两我的若是要通话,首先要创建链接——即打电话时的拨号,等待响应后——即接听电话后,才能相互传递信息,最后还要断开链接——即挂电话。写信就比较简单了,填写好收信人的地址后将信投入邮筒,收信人就能够收到了。从这个分析能够看出,创建链接能够在须要痛心地双方创建一个传递信息的通道,在发送方发送请求链接信息接收方响应后,因为是在接受方响应后才开始传递信息,并且是在一个通道中传送,所以接受方能比较完整地收到发送方发出的信息,即信息传递的可靠性比较高。但也正由于须要创建链接,使资源开销加大(在创建链接前必须等待接受方响应,传输信息过程当中必须确认信息是否传到及断开链接时发出相应的信号等),独占一个通道,在断开链接钱不能创建另外一个链接,即两人在通话过程当中第三方不能打入电话。而无链接是一开始就发送信息(严格说来,这是没有开始、结束的),只是一次性的传递,是先不须要接受方的响应,于是在必定程度上也没法保证信息传递的可靠性了,就像写信同样,咱们只是将信寄出去,却不能保证收信人必定能够收到。 TCP是面向链接的,有比较高的可靠性, 一些要求比较高的服务通常使用这个协议,如FTP、Telnet、SMTP、HTTP、POP3等。 而UDP是面向无链接的,使用这个协议的常见服务有DNS、SNMP、QQ等。对于QQ必须另外说明一下,QQ2003之前是只使用UDP协议的,其服务器使用8000端口,侦听是否有信息传来,客户端使用4000端口,向外发送信息(这也就不难理解在通常的显IP的QQ版本中显示好友的IP地址信息中端口常为4000或其后续端口的缘由了),即QQ程序既接受服务又提供服务,在之后的QQ版本中也支持使用TCP协议了。
当应用程序但愿经过 TCP 与另外一个应用程序通讯时,它会发送一个通讯请求。这个请求必须被送到一个确切的地址。在双方“握手”以后,TCP 将在两个应用程序之间创建一个全双工 (full-duplex) 的通讯。
这个全双工的通讯将占用两个计算机之间的通讯线路,直到它被一方或双方关闭为止。
TCP是因特网中的传输层协议,使用三次握手协议创建链接。当主动方发出SYN链接请求后,等待对方回答SYN+ACK[1],并最终对对方的 SYN 执行 ACK 确认。这种创建链接的方法能够防止产生错误的链接。[1] TCP三次握手的过程以下: 客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。 服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。 客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。 三次握手完成,TCP客户端和服务器端成功地创建链接,能够开始传输数据了。
创建一个链接须要三次握手,而终止一个链接要通过四次握手,这是由TCP的半关闭(half-close)形成的。 (1) 某个应用进程首先调用close,称该端执行“主动关闭”(active close)。该端的TCP因而发送一个FIN分节,表示数据发送完毕。 (2) 接收到这个FIN的对端执行 “被动关闭”(passive close),这个FIN由TCP确认。 注意:FIN的接收也做为一个文件结束符(end-of-file)传递给接收端应用进程,放在已排队等候该应用进程接收的任何其余数据以后,由于,FIN的接收意味着接收端应用进程在相应链接上再无额外数据可接收。 (3) 一段时间后,接收到这个文件结束符的应用进程将调用close关闭它的套接字。这致使它的TCP也发送一个FIN。 (4) 接收这个最终FIN的原发送端TCP(即执行主动关闭的那一端)确认这个FIN。[1] 既然每一个方向都须要一个FIN和一个ACK,所以一般须要4个分节。 注意: (1) “一般”是指,某些状况下,步骤1的FIN随数据一块儿发送,另外,步骤2和步骤3发送的分节都出自执行被动关闭那一端,有可能被合并成一个分节。[2] (2) 在步骤2与步骤3之间,从执行被动关闭一端到执行主动关闭一端流动数据是可能的,这称为“半关闭”(half-close)。 (3) 当一个Unix进程不管自愿地(调用exit或从main函数返回)仍是非自愿地(收到一个终止本进程的信号)终止时,全部打开的描述符都被关闭,这也致使仍然打开的任何TCP链接上也发出一个FIN。 不管是客户仍是服务器,任何一端均可以执行主动关闭。一般状况是,客户执行主动关闭,可是某些协议,例如,HTTP/1.0却由服务器执行主动关闭。[2]
6,UDP协议
当应用程序但愿经过UDP与一个应用程序通讯时,传输数据以前源端和终端不创建链接。
当它想传送时就简单地去抓取来自应用程序的数据,并尽量快地把它扔到网络上。
套接字(socket)初使用
1,基于TCP协议的socket
tcp是基于连接的,必须先启动服务端,而后再启动客户端去连接服务端
1.1,server端
import socket
sk = socket.socket() # 建立服务器scoket(接口)对象
sk.bind(('127.0.0.1',8898)) # 给本接口绑定地址,端口 # bind 捆绑
sk.listen() # 监听是否有连接请求
conn, addr = sk.accept() # 接受客户端连接,地址。建立连接对象conn
# print(conn,addr)
ret = conn.recv(1024) # recvive接收 客户端send发送信息;参数设置接:收多少字节
print(ret) # 收到的消息是bytes类型的
conn.send(b'helloy') # 向客户端发送信息,必须是bytes类型
conn.close() # 关闭客户端连接
sk.close() # 关闭服务器接口 # 可选
# accept 接受 akˈsept # connect 链接 kəˈnekt # address 地址 əˈdres,
# receive 接收 riˈsēv
1.2,client端
import socket
sk = socket.socket() # 建立客户端scoket(接口)对象
sk.connect(('127.0.0.1',8898)) # 经过本接口尝试连接要连接的服务器及它的接口
sk.send(b'hello!!') # 向服务器发送信息,必须是bytes类型
ret = sk.recv(1024)
print(ret) # 收到的消息是bytes类型的
sk.close() # 关闭客户端接口
1.3,问题:在重启服务端时可能会遇到
解决方法:
#加入一条socket配置,重用ip和端口
import socket
from socket import SOL_SOCKET,SO_REUSEADDR
sk = socket.socket() # 建立服务器scoket(接口)对象
sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
sk.bind(('127.0.0.1',8898)) # 给本接口绑定地址,端口 # bind 捆绑
sk.listen() # 监听是否有连接请求
conn, addr = sk.accept() # 接受客户端连接,地址。建立连接对象conn
# print(conn,addr)
ret = conn.recv(1024) # recvive接收 客户端send发送信息;参数设置接:收多少字节
print(ret) # 收到的消息是bytes类型的
conn.send(b'helloy') # 向客户端发送信息,必须是bytes类型
conn.close() # 关闭客户端连接
sk.close() # 关闭服务器接口 # 可选
# accept 接受 akˈsept # connect 链接 kəˈnekt # address 地址 əˈdres,
# receive 接收 riˈsēv
1.4,稍加完善
import socket sk = socket.socket() # 建立服务器scoket(接口/套接字)对象
sk.bind(('127.0.0.1',8898)) # 给本接口绑定地址,端口 # bind 捆绑
sk.listen() # 监听是否有连接请求
while True: conn, addr = sk.accept() # 接受客户端连接,地址。建立连接对象conn
while True: msg = conn.recv(1024).decode('utf-8') # recvive接收 客户端send发送信息;参数设置接:收多少字节;将接收的信息解码
print(msg) if msg == 'bye': #若是收到客户端发来的是bye,客户端想结束
break info = input('>>>') # 输入想向客户端发送的消息
if info == 'bye': # 若是服务端想结束
conn.send(b'bye') # 向客户端发送bye
break conn.send(info.encode('utf-8')) # 向客户端发送信息,必须是bytes类型
conn.close() # 关闭客户端连接
sk.close() # 关闭服务器接口,若是不关闭,还能继续接收
# accept 接受 akˈsept # connect 链接 kəˈnekt # address 地址 əˈdres, # receive 接收 riˈsēv
# 服务端只开了一个接口,一次只能与一个客户端创建链接, # # 当关闭该客户端链接后,才会与另外一个客户端创建链接
import socket sk = socket.socket() # 建立客户端scoket(接口)对象
sk.connect(('127.0.0.1',8898)) # 经过本接口尝试连接要连接的服务器及它的接口
while True: msg = input('>>>') # 输入要发送的消息
if msg == 'bye': # 若是想结束,发送bye
sk.send(b'bye') break sk.send(msg.encode('utf-8')) # 向服务器发送信息,必须是bytes类型
ret = sk.recv(1024).decode('utf-8') if ret == 'bye': break
print(ret) # 收到的消息是bytes类型的
sk.close() # 关闭客户端接口
import socket sk = socket.socket() # 建立客户端scoket(接口)对象
sk.connect(('127.0.0.1',8898)) # 经过本接口尝试连接要连接的服务器及它的接口
while True: msg = input('>>>') # 输入要发送的消息
if msg == 'bye': # 若是想结束,发送bye
sk.send(b'bye') break sk.send(('client2:' + msg).encode('utf-8')) # 向服务器发送信息,必须是bytes类型
ret = sk.recv(1024).decode('utf-8') if ret == 'bye': break
print(ret) # 收到的消息是bytes类型的
# 运行后,若是服务端已经与客户端创建链接,此处会等待, # 当服务端关闭链接后会与该客户端创建链接,并接收到发送的消息
2,基于UDP协议的socket
udp是无连接的,启动服务以后能够直接接受消息,不须要提早创建连接
2.1,server端
import socket
sk = socket.socket(type=socket.SOCK_DGRAM) #DGRAM datagram
sk.bind(('127.0.0.1',8080)) #只有服务端有的
msg,addr = sk.recvfrom(1024)
print(msg.decode('utf-8'))
sk.sendto(b'bye',addr)
sk.close()
# udp的server 不须要进行监听也不须要创建链接
# 在启动服务以后只能被动的等待客户端发送消息过来
# 客户端发送消息的同时还会 自带地址信息
# 消息回复的时候 不只须要发送消息,还须要把对方的地址填写上
2.2,client端
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
ip_port = ('127.0.0.1',8080)
sk.sendto(b'hello',ip_port)
ret,addr = sk.recvfrom(1024)
print(ret.decode('utf-8'))
sk.close()
# client端不须要connect 由于UDP协议是不须要创建链接的
# 直接了解到对方的ip和端口信息就发送数据就好了
# sendto和recvfrom的使用方法是彻底和server端一致的
3, qq聊天
3.1,简单实现
#_*_coding:utf-8_*_
import socket ip_prot = ('127.0.0.1', 8081) sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sk.bind(ip_prot) while True: qq_msg, addr = sk.recvfrom(1024) print('%s:%s:\033[1;44m%s\033[0m'%(addr[0],addr[1],qq_msg.decode('utf-8'))) back_msg = input('me:').strip() sk.sendto(back_msg.encode('utf-8'), addr)
#_*_coding:utf-8_*_
import socket BUFSIZE = 1024 # buffer 缓冲 [ˈbʌfə(r)]
sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) qq_name_dic = { 'Alice':('127.0.0.1',8081), '亚丝娜':('127.0.0.1',8081), '桐人':('127.0.0.1',8081), '尤吉欧':('127.0.0.1',8081), } while True: qq_name = input('请选择聊天对象:').strip() if not qq_name or qq_name not in qq_name_dic: print('不是您的好友!') continue
while True: msg = input('请输入消息,回车发送,输入q结束和他的聊天:').strip() if msg == 'q':break sk.sendto(msg.encode('utf-8'), qq_name_dic[qq_name]) back_msg, addr = sk.recvfrom(BUFSIZE) print('%s:%s\033[1;44m%s\033[0m'%(addr[0], addr[1],back_msg.decode('utf-8'))) sk.close() # 本qq例,无论人物有几个,每次只能与其中一我的物对话。 # 终端只建立了一个套接字对象,即终端的地址端口是一个,不是说一我的物一个端口。
3.2,稍加完善
#_*_coding:utf-8_*_
import socket ip_prot = ('127.0.0.1', 8080) sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(ip_prot) while True: qq_msg, addr = sk.recvfrom(1024) print('%s:%s'%(addr,qq_msg.decode('utf-8'))) back_msg = input('SAO:').strip() back_msg = ('\033[34mSAO :%s\033[0m'%back_msg).encode('utf-8') sk.sendto(back_msg, addr) sk.close() # 可分别与各终端互动, # 但本例中的服务端,在接收到一个终端的消息后必须回复该终端消息后才能接收新消息。 # 本服务端只能被动互动
import socket sk = socket.socket(type=socket.SOCK_DGRAM) ip_port = ('127.0.0.1',8080) while True: info = input('亚丝娜 :') info = ('\033[34m亚丝娜 :%s\033[0m'%info).encode('utf-8') sk.sendto(info,ip_port) msg,addr = sk.recvfrom(1024) print(msg.decode('utf-8')) sk.close()
import socket sk = socket.socket(type=socket.SOCK_DGRAM) ip_port = ('127.0.0.1',8080) while True: info = input('桐人 :') info = ('\033[32m桐人 :%s\033[0m'%info).encode('utf-8') sk.sendto(info,ip_port) msg,addr = sk.recvfrom(1024) print(msg.decode('utf-8')) sk.close()
4,时间服务器
# _*_coding:utf-8_*_
from socket import *
from time import strftime ip_port = ('127.0.0.1', 9000) bufsize = 1024 tcp_server = socket(AF_INET, SOCK_DGRAM) tcp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) tcp_server.bind(ip_port) while True: msg, addr = tcp_server.recvfrom(bufsize) print('===>', msg) if not msg: time_fmt = '%Y-%m-%d %X'
else: time_fmt = msg.decode('utf-8') back_msg = strftime(time_fmt) tcp_server.sendto(back_msg.encode('utf-8'), addr) tcp_server.close()
#_*_coding:utf-8_*_
from socket import * ip_port=('127.0.0.1',9000) bufsize=1024 tcp_client=socket(AF_INET,SOCK_DGRAM) while True: msg=input('请输入时间格式(例%Y %m %d)>>: ').strip() tcp_client.sendto(msg.encode('utf-8'),ip_port) data=tcp_client.recv(bufsize)
5,socket参数的详解
socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None)
建立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,则其余参数将被忽略,致使带有指定文件描述符的套接字返回。 与socket.fromfd()不一样,fileno将返回相同的套接字,而不是重复的。 这可能有助于使用socket.close()关闭一个独立的插座。 |
黏包问题
题:回答
# 为何会出现黏包现象?
# 首先只有在TCP协议中才会出现黏包现象,
# 是由于TCP协议是面向流的协议
# 在发送的数据传输的过程当中还有缓存机制来避免数据丢失
# 所以 在连续发送小数据的时候 以及接收大小不符的时候都容易出现黏包现象
# 本质仍是由于咱们在接收数据的时候不知道发送的数据的长短
# 解决黏包问题
# 在传输大量数据以前先告诉接收端要发送的数据大小
# 若是想更漂亮的解决问题,能够经过struct模块来定制协议
让咱们基于tcp先制做一个远程执行命令的程序(命令ls -l ; lllllll ; pwd)
res=subprocess.Popen(cmd.decode('utf-8'), shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 的结果的编码是以当前所在的系统为准的,若是是windows,那么res.stdout.read()读出的就是GBK编码的,在接收端须要用GBK解码 且只能从管道里读一次结果
同时执行多条命令以后,获得的结果极可能只有一部分,在执行其余命令的时候又接收到以前执行的另一部分结果,这种显现就是黏包。
1,基于tcp协议实现的黏包
#_*_coding:utf-8_*_
from socket import *
import subprocess ip_port=('127.0.0.1',8888) BUFSIZE=1024 tcp_socket_server=socket(AF_INET,SOCK_STREAM) tcp_socket_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) tcp_socket_server.bind(ip_port) tcp_socket_server.listen(5) while True: conn,addr=tcp_socket_server.accept() print('客户端',addr) while True: cmd=conn.recv(BUFSIZE) if len(cmd) == 0:break res=subprocess.Popen(cmd.decode('utf-8'),shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) stderr=res.stderr.read() stdout=res.stdout.read() conn.send(stderr) conn.send(stdout)
#_*_coding:utf-8_*_
import socket BUFSIZE=1024 ip_port=('127.0.0.1',8888) s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) res=s.connect_ex(ip_port) while True: msg=input('>>: ').strip() if len(msg) == 0:continue
if msg == 'quit':break s.send(msg.encode('utf-8')) act_res=s.recv(BUFSIZE) print(act_res.decode('utf-8'),end='')
2,基于udp协议实现的黏包
#_*_coding:utf-8_*_
from socket import *
import subprocess ip_port=('127.0.0.1',9000) bufsize=1024 udp_server=socket(AF_INET,SOCK_DGRAM) udp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) udp_server.bind(ip_port) while True: #收消息
cmd,addr=udp_server.recvfrom(bufsize) print('用户命令----->',cmd) #逻辑处理
res=subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=subprocess.PIPE,stdin=subprocess.PIPE,stdout=subprocess.PIPE) stderr=res.stderr.read() stdout=res.stdout.read() #发消息
udp_server.sendto(stderr,addr) udp_server.sendto(stdout,addr) udp_server.close()
from socket import * ip_port=('127.0.0.1',9000) bufsize=1024 udp_client=socket(AF_INET,SOCK_DGRAM) while True: msg=input('>>: ').strip() udp_client.sendto(msg.encode('utf-8'),ip_port) err,addr=udp_client.recvfrom(bufsize) out,addr=udp_client.recvfrom(bufsize) if err: print('error : %s'%err.decode('utf-8'),end='') if out: print(out.decode('utf-8'), end='')
注意:只有TCP有粘包现象,UDP永远不会粘包