1、TCP/IP
tcp/ip协议的英文全名:Transmission control protocol and internet protocol
tcp/ip协议是主机接入互联网以及接入互联网的两台主机通信额标准
1、物理层(Physical Layer) 物理层规定了激活、维持、关闭通信端点之间的机械特性、电气特性、功能特性以及过程特性。该层为上层协议提供了一个传输数据的物理媒体。 在这一层,数据的单位称为比特(bit)。 属于物理层定义的典型规范代表包括:EIA/TIA RS-232、EIA/TIA RS-449、V.35、RJ-45等。 2、数据链路层(Data Link Layer) 数据链路层在不可靠的物理介质上提供可靠的传输。该层的作用包括:物理地址寻址、数据的成帧、流量控制、数据的检错、重发等。 在这一层,数据的单位称为帧(frame)。 数据链路层协议的代表包括:SDLC、HDLC、PPP、STP、帧中继等。 3、网络层(Network Layer) 网络层负责对子网间的数据包进行路由选择。此外,网络层还可以实现拥塞控制、网际互连等功能。 在这一层,数据的单位称为数据包(packet)。 网络层协议的代表包括:IP、IPX、RIP、OSPF等。 4、传输层(Transport Layer) 传输层是第一个端到端,即主机到主机的层次。传输层负责将上层数据分段并提供端到端的、可靠的或不可靠的传输。此外,传输层还要处理端到端的差错控制和流量控制问题。 在这一层,数据的单位称为数据段(segment)。 传输层协议的代表包括:TCP、UDP、SPX等。 5、会话层(Session Layer) 会话层管理主机之间的会话进程,即负责建立、管理、终止进程之间的会话。会话层还利用在数据中插入校验点来实现数据的同步。 会话层协议的代表包括:NetBIOS、ZIP(AppleTalk区域信息协议)等。 6、表示层(Presentation Layer) 表示层对上层数据或信息进行变换以保证一个主机应用层信息可以被另一个主机的应用程序理解。表示层的数据转换包括数据的加密、压缩、格式转换等。 表示层协议的代表包括:ASCII、ASN.1、JPEG、MPEG等。 7、应用层(Application Layer) 应用层为操作系统或网络应用程序提供访问网络服务的接口。 应用层协议的代表包括:Telnet、FTP、HTTP、SNMP等。
我们看完了osi7层模型,就会想到tcp/ip5层模型,两个是相通的。
这个时候我们就引进socket的概念
Socket是一个软件抽象层,只是负责处理,但是不会去发送数据,发送数据的都是通过协议来进行的
上面这个图就是通过,socket实现的机器交互
socket的一些定义:
1.基于python3.5.2版本的socket只能收发字节(python2.7可以发送str)
2.退出只在客户端退出就ok了
3.s.accept()和s.recv()是阻塞的(基于链接正常)
4.listen(n) n代表:能挂起的链接数,如果n=1,代表可以链接一个,挂起一个,第三个拒绝
5.服务端出现端口冲突:修改监听端口号
6.
服户端:
1.send #数据长度
4.recv #收到确认信息,开始下一步发送
send
客户端:
2.recv #获取数据长度
3.send #发送确认信息
recv #循环接收
下面我们就来拿一个程序来进行学习:
Server端程序:
import socket ip_port=(‘127.0.0.1‘,9999)#必须是元组 s=socket.socket() s.bind(ip_port) s.listen(5) while True: conn,addr=s.accept() while True: try: rev_data=conn.recv(1024) if len(rev_data)==0:break send_data=rev_data.upper() print(send_data) conn.send(send_data) except Exception: break conn.close()
Client端:
import socket ip_port=(‘127.0.0.1‘,9999)#必须是元组 s=socket.socket() s.connect(ip_port) while True: send_data=input(">>:").strip() if send_data == "exit":break if len(send_data)==0:continue s.send(bytes(send_data,encoding=‘utf-8‘)) recv_data=s.recv(1024) print(str(recv_data,encoding=‘utf-8‘)) s.close()
当我先运行了server端的程序,然后在运行server端的程序:
通过程序我们可以知道,功能点就是把client端的输入转化为大写然后返回给client端。
>>:ls LS >>:lll LLL >>:
这个是客户端的输入和返回结果,我们再看一下server端的结果:
b‘LS‘ b‘LLL‘
这里面可以看出变量类型是byters
在退出的时候,我们需要注意一个地方,就是先暂停client然后再去停止server端,封装就会报端口占用的错误。
如果要避免这个错误,可以使用如下代码:
#1. 加上s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1), #如下代码 self.host=socket.gethostbyname(socket.gethostname()) s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((self.host,self.port)) s.listen(5)
socket编程的时候,发送的是字节,接受到的时候,不需要修改,那会直接把接受到的字节返回回去
recv和accept这两个地方都有堵塞的点(基于连接正常)
再来看一个程序
这个程序主要功能就是传输大数据的方法
Server端:
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author:wzc import socket import subprocess #导入执行命令模块 ip_port=(‘127.0.0.1‘,9999) #定义元祖 s=socket.socket() #绑定协议,生成套接字 s.bind(ip_port) #绑定ip+协议+端口:用来唯一标识一个进程,ip_port必须是元组格式 s.listen(5) #定义最大可以挂起胡链接数 while True: #用来重复接收新的链接 conn,addr=s.accept() #接收客户端的链接请求,返回conn(相当于一个特定胡链接),addr是客户端ip+port #收消息 while True: #用来基于一个链接重复收发消息 try: #捕捉客户端异常关闭(ctrl+c) recv_data=conn.recv(1024) #收消息,阻塞 if len(recv_data) == 0:break #客户端如果退出,服务端将收到空消息,退出 #发消息 p=subprocess.Popen(str(recv_data,encoding=‘utf8‘),shell=True,stdout=subprocess.PIPE) #执行系统命令,windows平 # 台命令的标准输出是gbk编码,需要转换 res=p.stdout.read() #获取标准输出 if len(res) == 0: #执行错误命令,标准输出为空, send_data=‘cmd err‘ else: send_data=str(res,encoding=‘gbk‘) #命令执行ok,字节gbk---->str---->字节utf-8 send_data=bytes(send_data,encoding=‘utf8‘) #解决粘包问题 ready_tag=‘Ready|%s‘ %len(send_data) conn.send(bytes(ready_tag,encoding=‘utf8‘)) #发送数据长度 feedback=conn.recv(1024) #接收确认信息 feedback=str(feedback,encoding=‘utf8‘) if feedback.startswith(‘Start‘): conn.send(send_data) #发送命令的执行结果 except Exception: break #挂电话 conn.close()
client端:
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author:Alex Li import socket ip_port=(‘127.0.0.1‘,9999) #买手机 s=socket.socket() #拨号 s.connect(ip_port) #链接服务端,如果服务已经存在一个好的连接,那么挂起 while True: #基于connect建立的连接来循环发送消息 send_data=input(">>: ").strip() if send_data == ‘exit‘:break if len(send_data) == 0:continue s.send(bytes(send_data,encoding=‘utf8‘)) #解决粘包问题 ready_tag=s.recv(1024) #收取带数据长度的字节:Ready|9998 ready_tag=str(ready_tag,encoding=‘utf8‘) if ready_tag.startswith(‘Ready‘):#Ready|9998 msg_size=int(ready_tag.split(‘|‘)[-1]) #获取待接收数据长度 start_tag=‘Start‘ s.send(bytes(start_tag,encoding=‘utf8‘)) #发送确认信息 #基于已经收到的待接收数据长度,循环接收数据 recv_size=0 recv_msg=b‘‘ while recv_size < msg_size: recv_data=s.recv(1024) recv_msg+=recv_data recv_size+=len(recv_data) print(‘MSG SIZE %s RECE SIZE %s‘ %(msg_size,recv_size)) print(str(recv_msg,encoding=‘utf8‘)) #挂电话 s.close()
这个程序是要注意的地方是“粘包”
在两侧send的时候,如果两次执行的时间间隔不长的话,就会出现端口占用的断后
使用多进程:
server端