python网络通信模块_Python socket网络模块

一、基于TCP协议的socket通信

以打电话为理解方式进行TCP的通信。

Server端代码:

importsocket

phone= socket.socket(socket.AF_INET, socket.SOCK_STREAM) #购买电话卡,AF_INET服务器之间网络通信,socket.SOCK_STREAM,流式协议,就是TCP协议

phone.bind(('127.0.0.1', 8080)) #选择电话号码,绑定IP地址

phone.listen(5) #开机,监控5个请求,最多能进入6个,第7个会报错

conn, addr = phone.accept() #等待接听电话,阻塞状态,获取IP和端口

print(conn, addr) #打印IP和端口

client_data = conn.recv(1024) #交流过程

print(client_data) #打印接收到的数据

conn.send(client_data.upper()) #接收到的英文字母变成大写并回传数据

conn.close()

phone.close()

Client端代码:

importsocket

phone=socket.socket()

phone.connect(('127.0.0.1', 8080))

msg= input('>>>').strip()

phone.send(msg.encode('utf-8'))

server_data= phone.recv(1024) #限制最大接收字节

print(server_data)

phone.close()

先运行Server端,再运行Client端,代码运行结果:

------------------------------------Client端运行结果---------------------------------------

>>>Others laugh at me for being mad. I laugh at others for notbeing able to see through. #>>>符号右侧为输入内容

b'OTHERS LAUGH AT ME FOR BEING MAD. I LAUGH AT OTHERS FOR NOT BEING ABLE TO SEE THROUGH.' #为服务端返回内容

------------------------------------------------------------------------------------------

------------------------------------Server端运行结果----------------------------------------

('127.0.0.1', 64096)

b'Others laugh at me for being mad. I laugh at others for not being able to see through.' #接收到客户端的内容

-------------------------------------------------------------------------------------------

二、单循环模式

Server端代码:

importsocket

phone= socket.socket(socket.AF_INET, socket.SOCK_STREAM) #使用TCP协议创建网络通信

phone.bind(('127.0.0.1', 8080)) #绑定IP地址和端口

phone.listen(5) #设置最大连接数,但是同一时间只能处理一个请求

while 1: #使服务端能够一直接受数据

conn, addr = phone.accept() #获取连接到服务器的IP和端口

try:

client_data= conn.recv(1024) #接收1024字节的数据

print(client_data) #打印数据(数字为bytes类型)

conn.send(client_data + b'-yes-') #源数据加上-yes-并返回给客户端

exceptException:breakconn.close()

phone.close()

Client端代码:

importsocket

phone=socket.socket()

phone.connect(('127.0.0.1', 8080))while 1:

msg= input('>>>').strip() #可输入要传输的字符

if msg.upper() == 'Q': #如果输出q则退出

break

elif not msg: #如果msg接到到的内容为空,则结束本次循环

continuephone.send(msg.encode('utf-8')) #使用utf-8的编码进行发送数据

server_data = phone.recv(1024) #最多只能接受1024字节,只问题会产生粘包

print(server_data.decode('utf-8')) #打印解码后的数据

phone.close()

先运行Server端,再运行Client端,代码运行结果:

------------------------------------Client端运行结果---------------------------------------

>>>laugh #输入要传到S端的内容

laugh-yes- #打印出来的是S端返回的内容

>>>crazy

crazy-yes-

>>>q

Process finished with exit code 0-------------------------------------------------------------------------------------------

------------------------------------Server端运行结果----------------------------------------

b'laugh'#收到C端发送来的内容

b'crazy'

-------------------------------------------------------------------------------------------

三、远程执行命令

3.1 远程执行命令之粘包现象的产生,示例如下:

Server端代码:

importsocketimport subprocess #远程执行命令需要使用到subprocess模块

phone =socket.socket(socket.AF_INET, socket.SOCK_STREAM)

phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)#(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket

phone.bind(('127.0.0.1', 8080))

phone.listen(5)while 1:

conn, addr=phone.accept()try:

cmd= conn.recv(1024)

obj= subprocess.Popen(cmd.decode('utf-8'), #要执行的命令或可执行文件的路径。一个由字符串组成的序列(通常是列表),列表的第一个元素是可执行程序的路径,\

#剩下的是传给这个程序的参数,如果没有要传给这个程序的参数,args 参数可以仅仅是一个字符串。

shell=True, #Linux下相当于执行/bin/bash,Windows下相当于执行cmd.exe

stdout=subprocess.PIPE, #指定子进程的标准输出

stderr=subprocess.PIPE, #指定子进程的标准错误输出

)

ret= obj.stdout.read() #把标准的输出结果赋值给ret变量

ret1 = obj.stderr.read() #把标准的错误输出结果赋值给ret1变量

conn.send(ret +ret1)exceptException:breakconn.close()

phone.close()

Client端代码:

importsocket

phone=socket.socket()

phone.connect(('127.0.0.1', 8080))while 1:

msg= input('>>>').strip()if msg.upper() == 'Q': #输出q则退出程序

break

elif notmsg:continue #解决的是输入为空程序会夯住的问题。

phone.send(msg.encode('utf-8')) #将接收到的数据进行编码后发送到服务端

server_data = phone.recv(1024) #每次只接收1024个字节

print(server_data.decode('gbk')) #打印接收数据

phone.close()

先运行Server端,再运行Client端,代码运行结果:

------------------------------------Client端运行结果---------------------------------------

>>>ipconfig /all #查看机器所有的IP地址

Windows IP 配置

主机名 . . . . . . . . . . . . . : WINDOWS-CP2ICQS

主 DNS 后缀 . . . . . . . . . . . :

节点类型 . . . . . . . . . . . . : 混合

IP 路由已启用 . . . . . . . . . . : 否

WINS 代理已启用 . . . . . . . . . : 否

以太网适配器 本地连接:

连接特定的 DNS 后缀 . . . . . . . :

描述. . . . . . . . . . . . . . . : Realtek PCIe GBE Family Controller

物理地址. . . . . . . . . . . . . :20-89-84-D1-E1-8B

DHCP 已启用 . . . . . . . . . . . : 否

自动配置已启用. . . . . . . . . . : 是

本地链接 IPv6 地址. . . . . . . . : fe80::91d8:554a:b7fb:f16e%13(首选)

IPv4 地址 . . . . . . . . . . . . :192.168.77.77(首选)

子网掩码 . . . . . . . . . . . . :255.255.255.0默认网关. . . . . . . . . . . . . :192.168.77.1DHCPv6 IAID . . . . . . . . . . . :287345028DHCPv6 客户端 DUID . . . . . . . :00-01-00-01-21-DE-4B-33-20-89-84-D1-E1-8B

DNS 服务器 . . . . . . . . . . . :114.114.114.114TCPIP 上的 NetBIOS . . . . . . . : 已启用

隧道适 #到此时数据明显没有接收全,因为每次只接收1024个字节>>>dir #直到发送下一个命令的时候,才把剩余的字节接收完成,这就是粘包现象之一配器 isatap.{D3F0159A-0A26-4494-9B2A-92BE10466E18}: #dir命令并未列出当前Windows目录的内容

媒体状态 . . . . . . . . . . . . : 媒体已断开

连接特定的 DNS 后缀 . . . . . . . :

描述. . . . . . . . . . . . . . . : Microsoft ISATAP Adapter

物理地址. . . . . . . . . . . . . :00-00-00-00-00-00-00-E0

DHCP 已启用 . . . . . . . . . . . : 否

自动配置已启用. . . . . . . . . . : 是

隧道适配器 Teredo Tunneling Pseudo-Interface:

媒体状态 . . . . . . . . . . . . : 媒体已断开

连接特定的 DNS 后缀 . . . . . . . :

描述. . . . . . . . . . . . . . . : Teredo Tunneling Pseudo-Interface

物理地址. . . . . . . . . . . . . :00-00-00-00-00-00-00-E0

DHCP 已启用 . . . . . . . . . . . : 否

自动配置已启用. . . . . . . . . . : 是>>>q

Process finished with exit code0

-------------------------------------------------------------------------------------------

3.2 什么是粘包:

所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。

tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端总是在收到ack时才会清除缓冲区内容。数据是可靠的,但是会粘包。

3.3 以下两种情况发发生粘包(只有tcp协议会产生粘包):

1,接收方没有及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

2,发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据也很小,会合到一起,产生粘包)

四、解决粘包的方法

4.1 使用struct模块

该模块可以把一个类型,如数字转换成固定长度的bytes

1480120-20181217160803690-1980282614.png

import struct

ret= struct.pack('i', 183346) #将一个int型数字转化成等长度的bytes类型。

print(ret, type(ret), len(ret))

----------------------------------------运行结果-------------------------------------------

b'2\xcc\x02\x00' 4------------------------------------------------------------------------------------------

ret1= struct.unpack('i', ret)[0] #通过unpack反解回来

print(ret1, type(ret1))

----------------------------------------运行结果-------------------------------------------183346 ------------------------------------------------------------------------------------------

ret= struct.pack('l', 4323241232132324) # 但是通过struct 处理不能处理太大

print(ret, type(ret), len(ret)) # 报错

----------------------------------------运行结果-------------------------------------------

File"D:/PycharmProjects/python/pack.py", line 21, in ret= struct.pack('l', 4323241232132324)

struct.error: argument out of range

------------------------------------------------------------------------------------------

4.2 方案代码如下:

整个流程的大致解释:

我们可以把报头做成字典,字典里包含将要发送的真实数据的描述信息(大小之类的),然后json序列化,然后用struck将序列化后的数据长度打包成4个字节。

我们在网络上传输的所有数据 都叫做数据包,数据包里的所有数据都叫做报文,报文里面不止有你的数据,还有ip地址、mac地址、端口号等等,其实所有的报文都有报头,这个报头是协议规定的,看一下

发送时:

先发报头长度

再编码报头内容然后发送

最后发真实内容

接收时:

先收报头长度,用struct取出来

根据取出的长度收取报头内容,然后解码,反序列化

从反序列化的结果中取出待取数据的描述信息,然后去取真实的数据内容

server端代码:

importsocketimportsubprocessimportstructimportjson

phone=socket.socket(socket.AF_INET, socket.SOCK_STREAM)

phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)

phone.bind(('127.0.0.1', 8080))

phone.listen(5)

conn, addr=phone.accept()while 1:try:

cmd= conn.recv(1024)

obj= subprocess.Popen(cmd.decode('utf-8'),

shell=True,

stdout=subprocess.PIPE,

stderr=subprocess.PIPE,

)

ret =obj.stdout.read() #1.获取执行命令返回的结果,计算结果的总大小

ret1=obj.stderr.read()

total_size= len(ret +ret1)

head_dict ={ #2.构建header字典'filename': 'file.txt','md5': '11111111111111','total_size': total_size,

}

dict_json =json.dumps(head_dict) #3.将字典转换json格式

dict_bytes = dict_json.encode('utf-8') #4.将字典转化成bytes

dict_len =len(dict_bytes) #5.计算字典bytes类型的大小

head_dict_len = struct.pack('i', dict_len) #6.将bytes类型的 字典的长度转化成固定长度的bytes

conn.send(head_dict_len) #7.发送字典的总大小

conn.send(dict_bytes) #8.发送 bytes类型的字典

conn.send(ret) #9.发送数据部分。

conn.send(ret1)exceptException:breakconn.close()

phone.close()

Client端代码:

importsocketimportstructimportjson

phone=socket.socket()

phone.connect(('127.0.0.1', 8080))while 1:

msg= input('>>>').strip()if msg.upper() == 'Q':break

elif notmsg:continuephone.send(msg.encode('utf-8'))

dict_size = struct.unpack('i', phone.recv(4))[0] #1.接收字典的大小。

header_dict_json = phone.recv(dict_size).decode('utf-8')#2.获取报头字典json格式。

header_dict =json.loads(header_dict_json) #3.通过json 反解成 字典data_size=0 #4.接收数据部分。

res= b''

while data_size < header_dict['total_size']:

data= phone.recv(1024)

res= res +data

data_size= data_size +len(data)print(res.decode('gbk'))

phone.close()

先运行Server端,再运行Client端,代码运行结果:

ContractedBlock.gif

ExpandedBlockStart.gif

>>>ipconfig /all

Windows IP 配置

主机名 . . . . . . . . . . . . . : WINDOWS-CP2ICQS

主 DNS 后缀 . . . . . . . . . . . :

节点类型 . . . . . . . . . . . . : 混合

IP 路由已启用 . . . . . . . . . . : 否

WINS 代理已启用 . . . . . . . . . : 否

以太网适配器 本地连接:

连接特定的 DNS 后缀 . . . . . . . :

描述. . . . . . . . . . . . . . . : Realtek PCIe GBE Family Controller

物理地址. . . . . . . . . . . . . :20-89-84-D1-E1-8B

DHCP 已启用 . . . . . . . . . . . : 否

自动配置已启用. . . . . . . . . . : 是

本地链接 IPv6 地址. . . . . . . . : fe80::91d8:554a:b7fb:f16e%13(首选)

IPv4 地址 . . . . . . . . . . . . :192.168.77.77(首选)

子网掩码 . . . . . . . . . . . . :255.255.255.0默认网关. . . . . . . . . . . . . :192.168.77.1DHCPv6 IAID . . . . . . . . . . . :287345028DHCPv6 客户端 DUID . . . . . . . :00-01-00-01-21-DE-4B-33-20-89-84-D1-E1-8B

DNS 服务器 . . . . . . . . . . . :114.114.114.114TCPIP 上的 NetBIOS . . . . . . . : 已启用

隧道适配器 isatap.{D3F0159A-0A26-4494-9B2A-92BE10466E18}:

媒体状态 . . . . . . . . . . . . : 媒体已断开

连接特定的 DNS 后缀 . . . . . . . :

描述. . . . . . . . . . . . . . . : Microsoft ISATAP Adapter

物理地址. . . . . . . . . . . . . :00-00-00-00-00-00-00-E0

DHCP 已启用 . . . . . . . . . . . : 否

自动配置已启用. . . . . . . . . . : 是

隧道适配器 Teredo Tunneling Pseudo-Interface:

媒体状态 . . . . . . . . . . . . : 媒体已断开

连接特定的 DNS 后缀 . . . . . . . :

描述. . . . . . . . . . . . . . . : Teredo Tunneling Pseudo-Interface

物理地址. . . . . . . . . . . . . :00-00-00-00-00-00-00-E0

DHCP 已启用 . . . . . . . . . . . : 否

自动配置已启用. . . . . . . . . . : 是>>>dir

驱动器 D 中的卷没有标签。

卷的序列号是 FCBB-EB88

D:\PycharmProjects\python\01.py 的目录2018/12/17 17:00

Process finished with exit code 0

View Code

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值