网络编程,开发练习题

Socket套接字方法

 

网络编制程序——socket开发,互联网编制程序socket

服务端:

1 什么是C/S架构

socket 实例类(8-10分钟)

socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)

 family***(*socke*t家族)***

  • socket.AF_UNIX:用于本机进度间通信,为了保险程序安全,八个单身的次第(进度)间是无法互相访问互相的内存的,但为了落到实处进度间的广播发表,能够通过创办三个地面包车型地铁socket来成功
  • socket.AF_INET:(还有AF_INET陆被用来ipv6,还有部分别样的地址家族,可是,他们依然是只用于有些平台,要么便是已经被放弃,可能是很少被使用,也许是一直未曾兑现,全部地点家族中,AF_INET是应用最普遍的2个,python支持很多样地址家族,可是出于大家只关心网络编制程序,所以超越八分之四时候笔者么只行使AF_INET)

 socket type***类型***

  • socket.SOCK_STREAM #for tcp
  • socket.SOCK_DGRAM #for udp
  • socket.SOCK_RAW
    #原始套接字,普通的套接字不可能处理ICMP、IGMP等互联网报文,而SOCK_RAW可以;其次,SOCK_RAW也能够处理格外的IPv四报文;别的,利用原始套接字,能够通过IP_HDLANDINCL套接字选项由用户构造IP头。
  • socket.SOCK_RDM
    #是一种保证的UDP情势,即确定保证交到数据报但不保证顺序。SOCK_RAM用来提供对原本协议的中低档访问,在须求实施某个特殊操作时采用,如发送ICMP报文。SOCK_RAM平日仅限于高级用户或领队运转的次第选择。
  • socket.SOCK_SEQPACKET #废弃了

(Only SOCK_STREAM and SOCK_DGRAM appear to be generally useful.)

 proto=0 请忽略,特殊用途

 fileno=None 请忽略,特殊用途

Socket套接字方法

 

  1. import socket

  2. server = socket.socket()

  3. #绑定要监听的端口

  4. server.bind((‘localhost’,6969))

  5. #监听

  6. server.listen()

  7. print(“等待连接…”)

  8. #伺机连接,conn正是客户端连过来在服务端为其生成的三个连连实例

c指的是client,S指的是Server,C/S框架结构的软件,实现服务端软件与客户端软件基于互联网通讯

服务端套接字函数(2分钟)

  • s.bind()
    绑定(主机,端口号)到套接字
  • s.listen()
    TCP开端监听传入连接。backlog内定在拒绝连接在此之前,能够挂起的最亚松森接数量。 backlog等于5,表示内核已经接到了连接请求,但服务器还未有调用accept实行处理的接连个数最大为5, 那几个值不能够Infiniti大,因为要在根本中爱惜连接队列
  • s.accept()
    被动接受TCP客户的总是,(阻塞式)等待连接的到来 

  服务程序调用accept函数从处于监听状态的流套接字s的客户连接请求队列中取出排在最前的一个客户请求,并且创建一个新的套接字来与客户套接字创建连接通道,如果连接成功,就返回新创建的套接字的描述符,以后与客户套接字交换数据的是新创建的套接字;如果失败就返回 INVALID_SOCKET。该函数的第一个参数指定处于监听状态的流套接字;操作系统利用第二个参数来返回新创建的套接字的地址结构;操作系统利用第三个参数来返回新创建的套接字的地址结构的长度。

 

socket 实例类(8-10分钟)

socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)

 

  1. conn,addr = server.accept()

  2. print(“连接实行中…”)

  3. print(conn,addr)

  4.  

  5. data = conn.recv(1024)

  6. print(“recv:”,data)

  7. conn.send(data.upper())

  8. server.close()

网络编程,开发练习题。二 网络协议是怎么样?分别介绍五层协议中每1层的意义

客户端套接字函数(贰分钟)

  • s.connect()
    主动伊始化TCP服务器连接
  • s.connect_ex()
    connect()函数的恢弘版本,出错开上下班时间回来出错码,而不是抛出特别

family(socket家族)

客户端:

互连网球组织议正是总结机界的报纸发表专业

公物用途的套接字函数(三-伍分钟)

  • s.recv()
    接收数据
  • s.send()
    发送数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完,可后边通超过实际例解释)
  • s.sendall()
    发送完整的TCP数据(本质正是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢掉,循环调用send直到发完)
  • s.recvfrom()
    Receive data from the socket. The return value is a pair (bytes,
    address)
  • s.getpeername()
    连接到当前套接字的远端的地方
  • s.close()
    关闭套接字
  • socket.setblocking(flag)
    #True or False,设置socket为非阻塞方式,今后讲io异步时会用
  • socket.getaddrinfo(host,
    port, family=0, type=0, proto=0, flags=0)
    重返远程主机的地点音信,例子
    socket.getaddrinfo(‘luffycity.com’,80)
  • socket.getfqdn()
    获得本机的主机名
  • socket.gethostbyname()
    通过域名解析ip地址

 

  • socket.AF_UNIX:用于本机进度间通信,为了有限支撑程序安全,八个独立的次第(进度)间是无法相互走访互相的内部存款和储蓄器的,但为了促成进度间的电视发表,能够由此创设一个本地的socket来成功
  • socket.AF_INET:(还有AF_INET陆被用于ipv六,还有局地其余的地点家族,可是,他们或许是只用于某些平台,要么正是现已被撇下,大概是很少被接纳,可能是一直未有实现,全体地方家族中,AF_INET是行使最广大的3个,python扶助很多样地点家族,但是由于大家只关怀网络编制程序,所以超过八分之四时候作者么只利用AF_INET)
  1. import socket

  2.  

  3. #扬言socket类型,同时生育socket连接对象

物理层成效:首假设根据电器天性发送高低电压,高电压对应数字1,低电压对应数字0

tcp套接字

1.简单套接字
客户端和服务端:两个主要功能,1、建立链接 2、数据通讯
服务端程序会产生两个套接字socket,一个用于三次握手建立链接,另一个用于收发消息数据通讯;
客户端产生一个套接字socket,既可以用于建立链接后,再用于收发消息数据通讯。

client.py

图片 1图片 2

 1 import socket
 2 
 3 #1.买手机
 4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 5 print(phone)
 6 
 7 #2.拨号
 8 phone.connect(('127.0.0.1',8081)) 
 9 #端口范围0-65535,0-1024给操作系统用的,若一直无法连接上server,则会一直停留在这一步
10 
11 #3.发收消息
12 phone.send('hello'.encode('utf-8'))
13 data = phone.recv(1024)
14 print(data)
15 
16 #4.关闭
17 phone.close()

View Code

 service.py

图片 3图片 4

 1 import socket
 2 
 3 #1.买手机
 4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 5 print(phone)
 6 
 7 #2.绑定手机卡
 8 phone.bind(('127.0.0.1',8081)) #端口范围0-65535,0-1024给操作系统用的
 9 
10 #3.开机
11 phone.listen(5) # 参数表示最大监听数
12 
13 #4.等电话链接
14 print('starting...')
15 conn,client = phone.accept() #返回一个新的套接字conn用于通讯,client为发起通讯链接的客户端的ip和端口号
16 print(conn,client)
17 # print('===>')
18 
19 #5.收,发消息
20 data = conn.recv(1024) # 单位:bytes,  1024代表最大接收1024个bytes
21 print('客户端的数据',data)
22 conn.send(data.upper())
23 
24 #6.挂电话
25 conn.close()
26 
27 #7.关机
28 phone.close()

View Code

 

  1. client = socket.socket()

  2. client.connect((‘localhost’,6969))

数量链路层的效果:定义了邮电通讯号的分组织承办法

 二.抬高循环套接字

client.py 

图片 5图片 6

 1 import socket
 2 
 3 #1.买手机
 4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 5 
 6 print(phone)
 7 
 8 #2.拨号
 9 phone.connect(('127.0.0.1',8080)) #端口范围0-65535,0-1024给操作系统用的
10 
11 while True:
12     msg = input('>>:').strip()
13     if not msg:continue
14     phone.send(msg.encode('utf-8'))  #phone.send(b'')
15     print('has send') #判断能否发空
16     data = phone.recv(1024)
17     print(data.decode('utf-8'))
18 
19 #4.关闭
20 phone.close()

View Code

service.py

图片 7图片 8

 1 import socket
 2 
 3 
 4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 5 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
 6 print(phone)
 7 phone.bind(('127.0.0.1',8080)) #端口范围0-65535,0-1024给操作系统用的
 8 phone.listen(5) #最大链接挂起数
 9 
10 print('starting...')
11 conn,client = phone.accept() #监听
12 # print('===>')
13 
14 #监听到到后,进行通讯循环
15 # while True:
16 #     data = conn.recv(1024) # 单位:bytes,  1024代表最大接收1024个bytes
17 #     #conn tcp协议三次握手的成果,双向链接
18 #     if not data:break #适用与linux操作,当client单方面终止链接时,service端会出现死循环
19 #     print('客户端的数据',data)
20 #     conn.send(data.upper())
21 
22 while True:
23     try:
24         data = conn.recv(1024) # 单位:bytes,  1024代表最大接收1024个bytes
25         #conn tcp协议三次握手的成果,双向链接
26 
27         print('客户端的数据',data)
28         conn.send(data.upper())
29     except ConnectionResetError:
30 
31         break
32 
33 conn.close()
34 phone.close()

View Code

if not data:break 是用于linux的判断,因为在linux中当client端单方面终止时,servce端会一直接收到空,会一直循环print('客户端的数据',data),因此需要加上判断;

except ConnectionResetError: 是针对windows的,当client端单方面终止时,server端会报ConnnectionRsetError。

有时重启服务端时会遇到报错:

图片 9

socket type类型

  1. client.send(b”hello world!”)

  2. data = client.recv(1024)

  3. print(“recv:”,data)

  4. client.close()

网络层功用:引进壹套新的地址用来不同不一致的广播域/子网,那套地址即网络地址

 

出于重启时系统还没来得及回收端口,因而会提醒端口已被占用。

其一是由于你的服务端还是存在捌回挥手的time_wait状态在挤占地址(假诺不懂,请深切研商一.tcp1遍握手,四遍挥手
二.syn洪流攻击
三.服务器高并发情状下会有大批量的time_wait状态的优化措施)

赶尽杀绝办法:出席一条socket配置,重用ip和端口。

phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
#就是它,在bind ip和端口 前加。

或者:

实验之前要全部关闭掉所用占用端口的程序,用以下指令
linux:pkill -9 python
windows:taskkill python

 

 

  • socket.SOCK_STREAM #for tcp
  • socket.SOCK_DGRAM #for udp
  • socket.SOCK_RAW
    #原始套接字,普通的套接字不可能处理ICMP、I达托霉素P等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也能够拍卖很是的IPv肆报文;此外,利用原始套接字,能够经过IP_HD路虎极光INCL套接字选项由用户构造IP头。
  • socket.SOCK_RDM
    #是一种保证的UDP方式,即确定保障交到数据报但不有限支撑顺序。SOCK_RAM用来提供对原本协议的中低档访问,在急需实施有个别特殊操作时采纳,如发送ICMP报文。SOCK_RAM常常仅限于高级用户或管理人运行的先后行使。
  • socket.SOCK_SEQPACKET #废弃了

服务端输出:

传输层成效:建立端口到端口的通信 补充:端口范围0-6553五,0-10二叁为系统占用端口

三.拉长 链接循环

事先代码运维可见,client端关闭后,service端也会关闭,但那时大家想client端关闭后,service端应该能在吸收接纳新的client端的链接请求,由此,在建

立链接的一些插足循环。

client.py 

图片 10图片 11

 1 import socket
 2 
 3 #1.买手机
 4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 5 
 6 print(phone)
 7 
 8 #2.拨号
 9 phone.connect(('127.0.0.1',8080)) #端口范围0-65535,0-1024给操作系统用的
10 
11 while True:
12     msg = input('>>:').strip()
13     if not msg:continue
14     phone.send(msg.encode('utf-8'))  #phone.send(b'')
15     print('has send') #判断能否发空
16     data = phone.recv(1024)
17     print(data.decode('utf-8'))
18 
19 #4.关闭
20 phone.close()

View Code

service.py

图片 12图片 13

 1 import socket
 2 
 3 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 4 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
 5 print(phone)
 6 phone.bind(('127.0.0.1',8080)) #端口范围0-65535,0-1024给操作系统用的
 7 phone.listen(5) #最大链接挂起数
 8 
 9 print('starting...')
10 
11 while True:
12     '''
13     用于监听多次client端的链接,但一次链接发起结束后,
14     可继续监听下一次client端的连接
15     '''
16     conn,client = phone.accept()
17     print(client)
18     while True:
19         try:
20             data = conn.recv(1024) # 单位:bytes,  1024代表最大接收1024个bytes
21             #conn tcp协议三次握手的成果,双向链接
22             if not data: break
23             print('客户端的数据',data)
24             conn.send(data.upper())
25         except ConnectionResetError:
26             break
27     conn.close()

View Code

 

(Only SOCK_STREAM and SOCK_DGRAM appear to be generally useful.)
  1. 等候连接…

  2. 连年进行中…

  3. <socket.socket fd=328,
    family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM,
    proto=0, laddr=(‘127.0.0.1’, 6969), raddr=(‘127.0.0.1’, 50248)>
    (‘127.0.0.1’, 50248)

  4. recv: b’hello world!’

应用层功用:将应用程序的数目打包传给传输层

 4.模拟ssh远程执行命令

client.py 

图片 14图片 15

 1 import socket,subprocess
 2 
 3 
 4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 5 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
 6 print(phone)
 7 phone.bind(('127.0.0.1',9900)) #端口范围0-65535,0-1024给操作系统用的
 8 phone.listen(5) #最大链接挂起数
 9 
10 print('starting...')
11 while True:
12     conn,client = phone.accept() #监听
13 
14     while True: #通讯循环
15         try:
16             #1、收命令
17             cmd = conn.recv(1024) # 单位:bytes,  1024代表最大接收1024个bytes
18             #conn tcp协议三次握手的成果,双向链接
19             if not cmd: break
20             #2、执行命令、拿到结果,命令的结果存入stdout=subprocess.PIPE管道,而不是直接输出到终端
21             obj = subprocess.Popen(cmd.decode('utf-8'),shell=True,
22                                    # 指令由client端发送过来是以utf-8解码为bytes发送过来的,因此处应该以utf-8来编码,
23                                    # 因此此处的命令编码应该与client端的一致
24                                    stdout=subprocess.PIPE,
25                                    stderr=subprocess.PIPE)
26             print(obj)
27             stdout = obj.stdout.read()
28             stderr =  obj.stderr.read() #s收发都是bytes格式
29 
30             #3、把命令的结果返回给客户端
31             conn.send(stdout+stderr) #申请一块新的内存空间存放stdout+stderr,会占内存,效率会低
32         except ConnectionResetError:
33             break
34     conn.close()
35 
36 phone.close()

View Code

service.py

图片 16图片 17

 1 import socket
 2 
 3 #1.买手机
 4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 5 
 6 print(phone)
 7 
 8 #2.拨号
 9 phone.connect(('127.0.0.1',9900)) #端口范围0-65535,0-1024给操作系统用的
10 
11 while True:
12     msg = input('>>:').strip()
13     if not msg:continue
14     phone.send(msg.encode('utf-8'))
15     # 注意:信息由utf-8解码为bytes格式发送到service端,因此service端也必须把bytes格式以utf-8来编码,
16 
17     data = phone.recv(1024)  #返回值可能超过1024bytes,
18     print(data.decode('gbk'))
19     # windows上,res.stdout.read()读出的就是GBK编码,因此此处也用GBK编码,linux上默认是utf-8
20 
21 #4.关闭
22 phone.close()

View Code

此地注意多少个小标题:

  1.service端的命令的编码应该与client端的解码格局对应,client端以utf-八解码指令为bytes,则service端必须以utf-八来编码;

  二.service端的把命令结果发送给client端,client则须求将指令结果举办编码,若serice端在windows上,则以GBK实行编码,若在linux上则以utf-八进行编码。

 

proto=0 请忽略,特殊用途

客户端输出:

三 基于tcp协议通讯,为什么建立链接必要一回握手,而断开链接却供给肆回挥手

5.粘包现象分析

须知:只有TCP有粘包现象,UDP永远不会粘包

图片 18

 

所谓粘包难题必不可缺依然因为接收方不清楚新闻之间的限度,不清楚1回性领取多少字节的数据所造成的。

udp的recvfrom是阻塞的,3个recvfrom(x)必须对唯一1个sendinto(y),收完了x个字节的数据就是完事,固然y>x数据就不见,那意味着udp根本不会粘包,可是会丢数据,不可信。

tcp的磋商数据不会丢,未有收完包,下次收受,会持续上次无冕接收,己端总是在吸收接纳ack时才会去掉缓冲区内容。数据是牢靠的,可是会粘包。

三种意况下会产生粘包:

发送端需求等缓冲区满才发送出去,造成粘包(发送数据时间间隔非常的短,数据段一点都不大,会师到1块儿,发生粘包)。TCP使用了优化措施(Nagle算法),将反复间距较小且数据量小的数目,合并成三个大的数据块,然后开始展览封包。

client.py

图片 19图片 20

1 import socket
2 
3 client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
4 client.connect(('127.0.0.1',9903))
5 
6 #TCP使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,
7 # 合并成一个大的数据块,然后进行封包。从而在发送端造成粘包。
8 client.send('hello'.encode('utf-8'))
9 client.send('world'.encode('utf-8'))

View Code

service.py

图片 21图片 22

 1 import socket
 2 
 3 service = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 4 service.bind(('127.0.0.1',9903))
 5 service.listen(5)
 6 
 7 conn,addr = service.accept()
 8 
 9 res1 = conn.recv(1024)
10 print('第一次',res1.decode())
11 
12 res2 =  conn.recv(1024)
13 print('第二次',res2.decode())

View Code

 输出结果:

第一次 helloworld
第二次 

发送端由于TCP
优化算法造成粘包

 

接收方不立时接收缓冲区的包,造成多少个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候照旧从缓冲区拿上次遗留的多少,发生粘包) 

 

client.py

图片 23图片 24

 1 #_*_coding:utf-8_*_
 2 __author__ = 'Linhaifeng'
 3 import socket
 4 BUFSIZE=1024
 5 ip_port=('127.0.0.1',8080)
 6 
 7 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 8 res=s.connect_ex(ip_port)
 9 
10 
11 s.send('hello feng'.encode('utf-8'))
12 
13 客户端

View Code

service.py

图片 25图片 26

 1 #_*_coding:utf-8_*_
 2 __author__ = 'Linhaifeng'
 3 from socket import *
 4 ip_port=('127.0.0.1',8080)
 5 
 6 tcp_socket_server=socket(AF_INET,SOCK_STREAM)
 7 tcp_socket_server.bind(ip_port)
 8 tcp_socket_server.listen(5)
 9 
10 
11 conn,addr=tcp_socket_server.accept()
12 
13 
14 data1=conn.recv(2) #一次没有收完整
15 data2=conn.recv(10)#下次收的时候,会先取旧的数据,然后取新的
16 
17 print('----->',data1.decode('utf-8'))
18 print('----->',data2.decode('utf-8'))
19 
20 conn.close()
21 
22 服务端

View Code

出口结果

-----> he
-----> llo feng

接收端由于没能3次将发送端2次发送的数码总体经受,导致粘包

 

拆包的爆发情状

当发送端缓冲区的长度超越网卡的MTU时,tcp会将此番发送的数码拆成多少个数据包发送出去。

填补问题一:为何tcp是牢靠传输,udp是不足靠传输

tcp在数码传输时,发送端先把多少发送到自个儿的缓存中,然后协议决定将缓存中的数据发往对端,对端再次回到二个ack=一,发送端则清理缓存中的数据,对端重返ack=0,则再次发送数据,所以tcp是保障的。

而udp发送数据,对端是不会回来确认音信的,因而不可靠。

补充难点二:send(字节流)和recv(十二肆)及sendall

recv里钦定的十二四意思是从缓存里2回拿出⑩24个字节的多少

send的字节流是先放入己端缓存,然后由协和式飞机决定将缓存内容发往对端,借使待发送的字节流大小大于缓存剩余空间,那么数量丢失,用sendall就会循环调用send,数据不会丢掉。

send 和 recv:
1.不管是recv还是send都不是直接接收对方的数据,而是操作自己的操作系统内存-->不是一个send对应一个recv
2.recv阶段,耗时分析:
  wait data 耗时非常长
  copy data 耗时短
  send耗时分析:
  copy data

 

fileno=None 请忽略,特殊用途

  1. recv: b’HELLO WORLD!’

三回握手:client发送请求建立通道;server收到请求并允许,同时也发送请求建通道;client收到请求并允许,建立完毕

5.粘包消除办法

粘包难题的来由在于接收端不领会发送端将要传送的字节流的长短,所以化解粘包的艺术正是围绕,如何让发送端在发送数据前,把团结快要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完全部数据。

率先步:先获得多少的长度

其次步:接收真实的数目

 先来介绍下struct模块

该模块能够把3个门类,如数字,转成固定长度的bytes类型

图片 27

 

但只顾类型如数字是有限制的,超出范围就会报错

>>>
struct.pack(‘i’,1111111111111)

。。。。。。。。。

struct.error:
‘i’ format requires -2147483648 <= number <= 2147483647
 #其1是限量

图片 28图片 29

1 import struct
2 
3 res = struct.pack('i', 1235)
4 print(res,type(res), len(res))
5 
6 obj = struct.unpack('i', res)
7 print(obj)

struct 用法

出口结果

b'\xd3\x04\x00\x00' <class 'bytes'> 4
(1235,)

在数码发送端将数据长度打包,发送给接收端,解包获取实际多少的尺寸。

 

 简单版本报头自制

client.py

图片 30图片 31

 1 import socket, struct
 2 
 3 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 4 print(phone)
 5 
 6 phone.connect(('127.0.0.1',9900)) #端口范围0-65535,0-1024给操作系统用的
 7 
 8 while True:
 9     #1、发命令
10     msg = input('>>:').strip()
11     if not msg:continue
12     phone.send(msg.encode('utf-8'))
13 
14     #2、拿到命令的结果,并打印
15 
16     #第一步:先收报头
17     header = phone.recv(4)
18     #第二步:从报头中解析出对真实数据的描述信息
19     total_size = struct.unpack('i',header)[0]
20 
21     #第三部:接收真实数据
22     recv_size = 0
23     recv_data = b''
24     while recv_size < total_size:
25         data = phone.recv(1024)
26         recv_data += data
27         recv_size += len(data)
28 
29     print(data.decode('gbk'))
30     # windows上,res.stdout.read()读出的就是GBK编码,因此此处也用GBK编码,linux上默认是utf-8
31 
32 phone.close()

自制报头

service.py

图片 32图片 33

 1 import socket,subprocess,struct
 2 
 3 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 4 phone.bind(('127.0.0.1',9900)) #端口范围0-65535,0-1024给操作系统用的
 5 phone.listen(5) #最大链接挂起数
 6 
 7 print('starting...')
 8 while True:
 9     conn,client = phone.accept() #监听
10 
11     while True: #通讯循环
12         try:
13             #1、收命令
14             cmd = conn.recv(1024) # 单位:bytes,  1024代表最大接收1024个bytes
15             #conn tcp协议三次握手的成果,双向链接
16             if not cmd: break
17             #2、执行命令、拿到结果,命令的结果存入stdout=subprocess.PIPE管道,而不是直接输出到终端
18             obj = subprocess.Popen(cmd.decode('utf-8'),shell=True,
19                                    stdout=subprocess.PIPE,
20                                    stderr=subprocess.PIPE)
21             print(obj)
22             stdout = obj.stdout.read()
23             stderr = obj.stderr.read() #s收发都是bytes格式
24 
25             #3、把命令的结果返回给客户端
26             #第一步:制作固定长度的报头
27             total_size = len(stdout)+len(stderr)
28             header = struct.pack('i', total_size)
29 
30             #第二步:把报头(固定长度)发送给客户端
31             conn.send(header)
32 
33             #第三步:再发送真实数据
34             conn.send(stdout)
35             conn.send(stderr)
36 
37         except ConnectionResetError:
38             break
39     conn.close()
40 
41 phone.close()

自制报头

 

高阶报头自制

以上讲解了简约报头的自制,但有缺陷:

1、报头存有的音信少。

二、struct模块打包的int数字有限量,普通指令的结果长度尽管不会超过这些限制,但是上传下载文件时就很有相当大也许会超过此限制,由此下边介绍同样选用struct模块来自制跟高端的报头。

以字典的形式构建报头,字典中得以存文件名、文件md伍值、文件大小等,再将字典系列化,将类别化的字符串长度通过struct
pack,这样既可让报头存有丰裕的音信,又不会当先struct模块打包的int的数字范围。

client.py

图片 34图片 35

 1 import socket, struct, json
 2 
 3 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 4 print(phone)
 5 
 6 phone.connect(('127.0.0.1',9900)) #端口范围0-65535,0-1024给操作系统用的
 7 
 8 while True:
 9     #1、发命令
10     msg = input('>>:').strip()
11     if not msg:continue
12     phone.send(msg.encode('utf-8'))
13 
14     #2、拿到命令的结果,并打印
15 
16     #第一步:先收报头长度
17     obj = phone.recv(4)
18     header_size = struct.unpack('i',obj)[0]
19     # 第二步:再收报头
20     header = phone.recv(header_size)
21 
22     #第三步:从报头中解析出对真实数据的描述信息
23     header_dic = json.loads(header)
24     total_size = header_dic['total_size']
25 
26     #第三部:接收真实数据
27     recv_size = 0
28     recv_data = b''
29     while recv_size < total_size:
30         data = phone.recv(1024)
31         recv_data += data
32         recv_size += len(data)
33 
34     print(data.decode('gbk'))
35     # windows上,res.stdout.read()读出的就是GBK编码,因此此处也用GBK编码,linux上默认是utf-8
36 
37 phone.close()

高阶报头自制

service.py

图片 36图片 37

 1 import socket, subprocess, struct, json
 2 
 3 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 4 phone.bind(('127.0.0.1',9900)) #端口范围0-65535,0-1024给操作系统用的
 5 phone.listen(5) #最大链接挂起数
 6 
 7 print('starting...')
 8 while True:
 9     conn,client = phone.accept() #监听
10 
11     while True: #通讯循环
12         try:
13             #1、收命令
14             cmd = conn.recv(1024) # 单位:bytes,  1024代表最大接收1024个bytes
15             #conn tcp协议三次握手的成果,双向链接
16             if not cmd: break
17             #2、执行命令、拿到结果,命令的结果存入stdout=subprocess.PIPE管道,而不是直接输出到终端
18             obj = subprocess.Popen(cmd.decode('utf-8'),shell=True,
19                                    stdout=subprocess.PIPE,
20                                    stderr=subprocess.PIPE)
21             print(obj)
22             stdout = obj.stdout.read()
23             stderr = obj.stderr.read() #s收发都是bytes格式
24 
25             #3、把命令的结果返回给客户端
26             #第一步:制作报头
27 
28             header_dic = {
29                 'filename':'a.txt',
30                 'md5':'xxfdxxx',
31                 'total_size': len(stdout)+len(stderr)
32             }
33             header_json = json.dumps(header_dic)
34             header_types = header_json.encode('utf-8')
35 
36             #第二步:把报头(固定长度)发送给客户端
37             conn.send(struct.pack('i',len(header_types)))
38 
39             #第三步:再发送报头、
40             conn.send(header_types)
41 
42             #第四步:再发送真实数据
43             conn.send(stdout)
44             conn.send(stderr)
45 
46         except ConnectionResetError:
47             break
48     conn.close()
49 
50 phone.close()

高阶报头自制

劳务端套接字函数(2分钟)

  • s.bind() 绑定(主机,端口号)到套接字
  • s.listen() 开始TCP监听
  • s.accept() 被动接受TCP客户的总是,(阻塞式)等待连接的过来

连天发送新闻:

4次挥手:client发送请求断开通道;server收到请求并同意,不过此时sever恐怕还在发多少,并不关门接口,全数回复同意和发送sever断开请求不是同台发送的;等到数量发送实现server也发送请求断开通道;client受到音讯甘休

 udp 协议套接字

客户端套接字函数(2秒钟)

  • s.connect() 主动开始化TCP服务器连接
  • s.connect_ex()
    connect()函数的恢弘版本,出错开上下班时间回来出错码,而不是抛出13分

服务端:

四 为什么基于tcp协议的通讯比基于udp共同商议的通讯更可相信?

tcp基于链接通讯

  • 听大人说链接,则须要listen(backlog),钦定连接池的高低
  • 基于链接,必须先运维的服务端,然后客户端发起链接请求
  • 对此mac系统:假设壹端断开了链接,那别的1端的链接也随即完蛋recv将不会卡住,收到的是空(消除措施是:服务端在收音信后增加if判断,空新闻就break掉通讯循环)
  • 对此windows/linux系统:借使壹端断开了链接,这此外一端的链接也随之完蛋recv将不会堵塞,收到的是空(化解措施是:服务端通讯循环内加1贰分处理,捕捉到非凡后就break掉通信循环)

国有用途的套接字函数(3-陆分钟)

  • s.recv() 接收数据
  • s.send()
    发送数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完,可后边通超过实际例解释)
  • s.sendall()
    发送完整的TCP数据(本质正是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢掉,循环调用send直到发完)
  • s.recvfrom() Receive data from the socket. The return value is a
    pair (bytes, address)
  • s.getpeername() 连接到当前套接字的远端的地点
  • s.close() 关闭套接字
  • socket.setblocking(flag) #True or
    False,设置socket为非阻塞方式,未来讲io异步时会用
  • socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)
    再次回到远程主机的地址新闻,例子 socket.getaddrinfo(‘luffycity.com’,80)
  • socket.getfqdn() 得到本机的主机名
  • socket.gethostbyname() 通过域名解析ip地址
  1. import socket

  2. client = socket.socket()

  3. client.connect((‘localhost’,6969))

tcp协议是面向链接的磋商,在通讯进程中,双方由此三回握手建立连接、肆回挥手断开连接,发送方给接收方发送数据,倘使未有赢得接收方的回复,就会持续给它发信息,直到接收方回应。

udp无链接

udp 不需要经过3次握手和4次挥手,不需要提前建立连接,直接发数据就行。
  • 无链接,由此无需listen(backlog),越发未有怎么连接池之说了
  • 无链接,udp的sendinto不用管是还是不是有八个正值周转的服务端,能够己端一个劲的发音讯,只可是数据丢失
  • recvfrom收的数额低于sendinto发送的数额时,在mac和linux系统上数据直接丢掉,在windows系统上发送的比收受的大学一年级直报错
  • 只有sendinto发送数据未有recvfrom收多少,数据丢失
  • 三个sendto对应多少个recvfrom,不会发出粘包难点

     
 udp协议就算不会发出粘包,但
udp协议不安全,tcp协议会在发多少前发个音信,在接收端回复确认能够接收数据后,才会发送数据,发送数据
       
 后还要静观其变接收端回复已吸收接纳后才会再而三发送,因而tcp协议是政通人和安全的。

client.py 

图片 38图片 39

 1 import socket
 2 # 建立套接字对象
 3 client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
 4 #client.connect(('127.0.0.0',8080)) #udp没有链接
 5 
 6 
 7 while True:
 8     msg = input('>>:').strip()
 9     client.sendto(msg.encode('utf-8'),('127.0.0.1',8080)) #udp没有链接,发送信息必须指定ip和端口
10 
11     data,server_addr = client.recvfrom(1024)
12     print(data,server_addr)
13 
14 client.close()

udp套接字 client

>>:hah
b'HAH' ('127.0.0.1', 8080)
>>:yy
b'YY' ('127.0.0.1', 8080)
>>:

service.py

图片 40图片 41

 1 import socket
 2 # 建立套接字对象
 3 server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
 4 server.bind(('127.0.0.1',8080))
 5 
 6 #server.listen() #监听最大链接数,udp没有链接
 7 #server.accept() #建立链接,udp无链接
 8 
 9 while True:
10     data,client = server.recvfrom(1024)
11     print(data,client) #返回数据和数据发送端的ip和端口
12     server.sendto(data.upper(),client) #udp没有链接,发送信息必须指定ip和端口
13 
14 server.close()

udp套接字 server

b'hah' ('127.0.0.1', 59001)
b'yy' ('127.0.0.1', 59001)

  

 

 

 

tcp套接字

1.简单套接字
客户端和服务端:两个主要功能,1、建立链接 2、数据通讯
服务端程序会产生两个套接字socket,一个用于三次握手建立链接,另一个用于收发消息数据通讯;
客户端产生一个套接字socket,既可以用于建立链接后,再用于收发消息数据通讯。

client.py 

图片 42

 1 import socket
 2 
 3 #1.买手机
 4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 5 print(phone)
 6 
 7 #2.拨号
 8 phone.connect(('127.0.0.1',8080)) #端口范围0-65535,0-1024给操作系统用的
 9 
10 #3.发收消息
11 phone.send('hello'.encode('utf-8'))
12 data = phone.recv(1024)
13 print(data)
14 
15 #4.关闭
16 phone.close()

View Code

 service.py

图片 43

 1 import socket
 2 
 3 #1.买手机
 4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 5 print(phone)
 6 
 7 #2.绑定手机卡
 8 phone.bind(('127.0.0.1',8081)) #端口范围0-65535,0-1024给操作系统用的
 9 
10 #3.开机
11 phone.listen(5) # 参数表示最大监听数
12 
13 #4.等电话链接
14 print('starting...')
15 conn,client = phone.accept() #返回一个新的套接字conn用于通讯,client为发起通讯链接的客户端的ip和端口号
16 print(conn,client)
17 # print('===>')
18 
19 #5.收,发消息
20 data = conn.recv(1024) # 单位:bytes,  1024代表最大接收1024个bytes
21 print('客户端的数据',data)
22 conn.send(data.upper())
23 
24 #6.挂电话
25 conn.close()
26 
27 #7.关机
28 phone.close()

View Code

  1. while
    True:

  2.     msg = input(“>>:”).strip()

  3.     client.send(msg.encode(“utf-8”))

  4.     data = client.recv(1024)

  5.     print(“recv:”,data.decode())

  6. client.close()

udp是面向数据报的情商,不需求贰遍握手建立连接,它不会管接收方有未有接受数额