socket编程进阶

简介: 1、   动态导入模块 第一种方法(python解释器自己内部用的): 上图是我程序的目录结构 下面代码是动态导入模块3.py的源码: 1 #AUTHOR:FAN 2 lib_dir = __import__('lib.

1、   动态导入模块

第一种方法(python解释器自己内部用的):

上图是我程序的目录结构

下面代码是动态导入模块3.py的源码:

1 #AUTHOR:FAN
2 lib_dir = __import__('lib.aa')
3 print(lib)
4 obj = lib.aa.C()
5 print(obj.name)

lib包目录下aa.py的源码如下:

1 #AUTHOR:FAN
2 
3 class C:
4     def __init__(self):
5         self.name = "dean"

这个时候运行动态导入模块.py程序,运行结果如下:

1 D:\python35\python.exe D:/python培训/s14/day8/动态导入模块3.py
2 <module 'lib' from 'D:\\python培训\\s14\\day8\\lib\\__init__.py'>
3 dean
4 Process finished with exit code 0

对上述动态导入模块3.py代码进行分析:

lib_dir = __import__('lib.aa')

print(lib)

打印出的内容是:

<module 'lib' from 'D:\\python培训\\s14\\day8\\lib\\__init__.py'>

从打印结果可以看出__import__('lib.aa')这个得到的就是lib目录

所以这个时候想要调用lib目录下aa中类中的数据直接:

obj = lib.aa.C()

print(obj.name)

这样就取得了aa.py程序中C类中初始化参数的name的值

第二种方法(官方建议):

程序目录如下:

动态导入模块4.py的代码如下:

1 #AUTHOR:FAN
2 import importlib
3 aa = importlib.import_module('lib.aa')
4 print(aa)
5 obj = aa.C()
6 print(obj.name)

lib目录下的aa.py不变化还是上述相同

这个时候运行动态导入模块4.py,运行结果如下:

1 D:\python35\python.exe D:/python培训/s14/day8/动态导入模块4.py
2 <module 'lib.aa' from 'D:\\python培训\\s14\\day8\\lib\\aa.py'>
3 dean
4 
5 Process finished with exit code 0

对上述代码进行分析:

aa = importlib.import_module('lib.aa')

print(aa)

打印出的内容如下:

<module 'lib.aa' from 'D:\\python培训\\s14\\day8\\lib\\aa.py'>

可以看出打印出的是lib.aa,所以这个时候可以直接实例化aa.C,并取得类中初始化参数中的name的值:

obj = aa.C()

print(obj.name)

通过上述两种方法也可以得出,两者虽然最终结果是相同的,但是过程中还是有区别的:

第一种方法:lib_dir = __import__('lib.aa'),这种方法得到的module是lib

第二种方法:

aa = importlib.import_module('lib.aa')这种方法得到的module是lib.aa

 

2、断言assert

 

先看如下代码:

1 #AUTHOR:FAN
2 name = "dean"
3 assert type(name) is str
4 print(name)

运行结果如下:

1 D:\python35\python.exe D:/python培训/s14/day8/断言.py
2 dean
3 
4 Process finished with exit code 0

断言其实就是对值进行判断,看是否满足条件,如果满足就向下执行,如果不满足就报错,我们将代码进行更改,再次运行:

1 #AUTHOR:FAN
2 name = 123
3 assert type(name) is str
4 print(name)

运行结果如下:

1 D:\python35\python.exe D:/python培训/s14/day8/断言.py
2 Traceback (most recent call last):
3   File "D:/python培训/s14/day8/断言.py", line 5, in <module>
4     assert type(name) is str
5 AssertionError
6 
7 Process finished with exit code 1

3、Socket

socket通常也称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过“套接字”向网络发出请求或者应答网络请求

下面是一些功能:

我们再通常使用socket的时候都需要先导入socket模块

即import socket,然后实例化socket,例如:

sk = socket(socket.AF_INET,socket.SOCK_STREAM,0)

参数一:地址簇

在这个参数中包含以下几个参数:

socket.AF_INET  表示IPV4(默认)

socket.AF_INET6 表示IPV6

socket.AF_UNIX   只能用于单一的Unix系统进程间的通信

参数二:类型

socket.SOCK_STREAM  流式socket for TCP(默认)

socket.SOCK_DGRAM   数据格式socket,for UDP

socket.SOCK_RAW     原始套接字,普通的套接字无法处理ICMP,IGMP等网络报文,可以通过IP_HDRINCL套接字选项由用户构造IP头

socket.SOCK_RDM      是一种可靠的UDP形式,即保证交付数据报但不保证顺序,SOCK_RDM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文,SOCK_RAM通常仅限于高级用户或管理员运行的程序使用

socket.SOCK_SEQPACKET  可靠的连续数据包服务

参数三:协议

默认与特定地址家族相关的协议,如果是0 则系统就会根据地址格式和套接类别,自动选择一个合适的协议

sk.bind((ip地址,port端口))

这种是在默认的AF_INET下这样以元组的形式存在即(ip,port)

sk.listen(backlog)

开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量

backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5

这个值不能无限大,因为需要在内核中维护连接队列

sk.setblocking(bool)

是否阻塞(默认为True),如果设置False,那么accept和recv时一旦无数据,就会报错

sk.accept()

接收连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据,address是连接客户端的地址

接收TCP客户端的连接(阻塞)等待连接的到来

sk.connect(address)

连接到address处的套接字,一般,address的格式为元组(hostname,port),如果连接出错,返回socket,error错误

sk.connect_ex(address)

同上的sk.connect只不过会有返回值,连接成功时返回0,连接失败时返回编码

sk.close()

关闭套接字

sk.recv(bufsize[,flag])

接收套接字的数据,数据以字符串形式返回,bufsize指定最多可以接收的数量,flag提供有关消息的其他信息,通常可以忽略

sk.recvfrom(bufsize[.flag])

与recv()类似,但返回值是(data,address)其中data是包含接收数据的字符串,address是发送数据的套接字地址

sock.send(string[,flag])

将string中的数据发送到连接的套接字,返回值是要发送的字节数量,该数量可能小于string的字节大小,即:可能未将指定内容全部发送

sk.sendall(string[,flag])

将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有的数据,成功返回None,失败则抛出异常

内部通过递归调用send,将所有内容发送出去

sk.sendto(string[,flag],address)

将数据发送到套接字,address是形式为(ip地址,port)的元组,指定远程地址,返回值时发送的字节数,该函数主要用于UDP协议

sk.settimeout(timeout)

设置套接字操作的超时期,timeout是一个浮点数,但是为秒

值为None表示没有超时期,一般超时期应该在刚创建套接字时设置,因为他们可能用于连接的操作

sk.getpeername()

返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。

sk.fileno()

套接字的文件描述符

用socket写一个简单的类似ssh的工具:

服务端:

 1 #AUTHOR:FAN
 2 import socket,os
 3 
 4 server = socket.socket()
 5 server.bind(('127.0.0.1',9999))
 6 
 7 server.listen()
 8 
 9 while True:
10     conn,addr = server.accept()
11     print("一个新的连接:",addr)
12     while True:
13         print("等待新指令")
14         data = conn.recv(1024)
15         if not data:
16             print("客户端已经断开")
17             break
18         print("执行指令:",data)
19         cmd_res = os.popen(data.decode()).read()
20         print("send before")
21         if len(cmd_res) == 0:
22             cmd_res = "cmd has no output......"
23         conn.send(cmd_res.encode())
24         print("send done")
25 server.close()

客户端:

 1 #AUTHOR:FAN
 2 import socket,os
 3 
 4 client = socket.socket()
 5 
 6 client.connect(('127.0.0.1',9999))
 7 
 8 while True:
 9         cmd = input(">>:").strip()
10         if len(cmd) == 0:continue
11         client.send(cmd.encode("utf-8"))
12         cmd_res = client.recv(1024)
13         print(cmd_res.decode())
14 client.close()

先启动服务端,在启动客户端,并在客户端执行ipconfig命令(先在windows上测试),运行结果如下:

客户端运行后结果显示:

 1 D:\python35\python.exe D:/python培训/s14/day8/sock_ssh_client.py
 2 >>:ipconfig
 3 
 4 Windows IP 配置
 5 
 6 
 7 以太网适配器 Bluetooth 网络连接:
 8 
 9    媒体状态  . . . . . . . . . . . . : 媒体已断开
10    连接特定的 DNS 后缀 . . . . . . . : 
11 
12 以太网适配器 本地连接:
13 
14    媒体状态  . . . . . . . . . . . . : 媒体已断开
15    连接特定的 DNS 后缀 . . . . . . . : 
16 
17 无线局域网适配器 无线网络连接:
18 
19    连接特定的 DNS 后缀 . . . . . . . : DHCP HOST
20    本地链接 IPv6 地址. . . . . . . . : fe80::85b7:6c65:f032:d29%12
21    IPv4 地址 . . . . . . . . . . . . : 192.168.1.102
22    子网掩码  . . . . . . . . . . . . : 255.255.255.0
23    默认网关. . . . . . . . . . . . . : 192.168.1.1
24 
25 以太网适配器 VMware Network Adapter VMnet1:
26 
27    连接特定的 DNS 后缀 . . . . . . . : 
28    本地链接 IPv6 地址. . . . . . . . : fe80::9c7d:99a2:b09b:fa49%15
29    IPv4 地址 . . . . . . . . . . . . : 10.0.10.22
30    子网掩码  . . . . . . . . . . . . : 255.255.255.0
31    默认网关. . . . . . . . . . . . . : 
32 
33 以太网适配器 VMware Network Adapter 
34 >>:dir
35 VMnet8:
36 
37    连接特定的 DNS 后缀 . . . . . . . : 
38    本地链接 IPv6 地址. . . . . . . . : fe80::4d7:4817:776d:6386%16
39    IPv4 地址 . . . . . . . . . . . . : 172.16.1.200
40    子网掩码  . . . . . . . . . . . . : 255.255.255.0
41    默认网关. . . . . . . . . . . . . : 
42 
43 隧道适配器 isatap.DHCP HOST:
44 
45    媒体状态  . . . . . . . . . . . . : 媒体已断开
46    连接特定的 DNS 后缀 . . . . . . . : DHCP HOST
47 
48 隧道适配器 Teredo Tunneling Pseudo-Interface:
49 
50    媒体状态  . . . . . . . . . . . . : 媒体已断开
51    连接特定的 DNS 后缀 . . . . . . . : 
52 
53 隧道适配器 isatap.{AEC4AF99-6E8C-4696-B0CE-1044479986E4}:
54 
55    媒体状态  . . . . . . . . . . . . : 媒体已断开
56    连接特定的 DNS 后缀 . . . . . . . : 
57 
58 隧道适配器 isatap.{E8300FF5-2E5A-4E63-8F58-33282553726B}:
59 
60    媒体状态  . . . . . . . . . . . . : 媒体已断开
61    连接特定的 DNS 后缀 . . . . . . . : 
62 
63 >>:

服务端显示:

 1 D:\python35\python.exe D:/python培训/s14/day8/sock_ssh_server.py
 2 一个新的连接: ('127.0.0.1', 62969)
 3 等待新指令
 4 执行指令: b'ipconfig'
 5 send before
 6 send done
 7 等待新指令
 8 执行指令: b'dir'
 9 send before
10 send done
11 等待新指令

从上面我们可以看出,当我们执行ipconfig命令的时候,显示的内容并不全,当再次执行dir命令的时候刚才没有显示的部分,从这里也可以看出,ipconfig第一次没有显示的内容被放在了缓冲区里,当dir执行的时候,先将缓冲区的内容显示,而这个时候dir命令显示的内容又被存在了缓冲区中………

所以需要解决上述问题:

解决的思路就是在发送数据之前需要将要发送文件的大小先发送过去,这样客户端根据收到数据的大小和这个文件的大小比较,直到收完为止(切记一个问题,整数不能直接encode()必须转换成字符串)

服务端代码如下:

 1 #AUTHOR:FAN
 2 import socket,os
 3 
 4 server = socket.socket()
 5 server.bind(('127.0.0.1',9999))
 6 server.listen()
 7 while True:
 8         conn,addr = server.accept()
 9         print("一个新的连接:",addr)
10         while True:
11             print("等待新指令")
12             data = conn.recv(1024)
13             if not data:
14                 print("客户端已经断开")
15                 break
16             print("执行指令:",data)
17             cmd_res = os.popen(data.decode()).read()
18             print("send before")
19             if len(cmd_res) == 0:
20                 cmd_res = "cmd has no output......"
21             conn.send(str(len(cmd_res)).encode())
22             conn.send(cmd_res.encode())
23             print("send done")
24 server.close()

客户端代码如下:

 1 #AUTHOR:FAN
 2 import socket,os
 3 
 4 client = socket.socket()
 5 
 6 client.connect(('127.0.0.1',9999))
 7 
 8 while True:
 9         cmd = input(">>:").strip()
10         if len(cmd) == 0:continue
11         client.send(cmd.encode("utf-8"))
12         cmd_res_size = client.recv(1024)
13         print("命令结果大小:",cmd_res_size)
14     #received_data = b''
15     received_data_size = 0
16     while received_data_size < int(cmd_res_size.decode()):
17         data = client.recv(1024)
18         received_data_size+= len(data)
19         #print(data.decode())
20         #cmd_res = client.recv(1024)
21         print(received_data_size)
22         # print(cmd_res.decode())
23     else:
24         print("cmd res receive done")
25 client.close()

这个时候现将收到命令结果给注释,先打印收到服务端发送命令数据的大小,已经自己收到的数据累加的大小

同样先启动服务端再启动客户端

运行结果如下:(下面是客户端的测试结果)

 1 D:\python35\python.exe D:/python培训/s14/day8/sock_ssh_client.py
 2 >>:ipconfig
 3 命令结果大小: b'1494'
 4 1024
 5 1960
 6 cmd res receive done
 7 >>:dir
 8 命令结果大小: b'734'
 9 852
10 cmd res receive done
11 >>:ipconfig /all
12 命令结果大小: b'4660'
13 1024
14 2048
15 3072
16 4096
17 5120
18 cmd res receive done
19 >>

从户可以看出,服务端发送给客户端命令结果的大小和客户端实际通过多次收到数据的总和不匹配,出现了客户端收到的数据大小大于服务器端发送过来的大小。

将服务端代码放到linux系统上执行,在windows运行客户端,运行结果如下:

 1 D:\python35\python.exe D:/python培训/s14/day8/sock_ssh_client.py
 2 >>:pwd
 3 命令结果大小: b'11'
 4 11
 5 cmd res receive done
 6 >>:ifconfig
 7 命令结果大小: b'902'
 8 902
 9 cmd res receive done
10 >>:

从这里可以看出是相同的了,并且判断出是因为在windows下有中文,同时在服务端发送的时候,有点问题,服务端代码的问题是下面部分:

conn.send(str(len(cmd_res)).encode())

这个发送的时候应该先改为:

conn.send(str(len(cmd_res.encode())).encode())

这样就解决了汉字发送后大小不匹配的问题

这样重新在windows上测试服务端程序和客户端程序:

更改之后客户端的运行结果如下:

1 D:\python35\python.exe D:/python培训/s14/day8/sock_ssh_client.py
2 >>:ipconfig
3 命令结果大小: b'1960'
4 1024
5 1960
6 cmd res receive done
7 >>:

这样就完美的解决了之前的问题

这样重新将服务端整理好的代码如下:

服务端代码如下:

 1 #AUTHOR:FAN
 2 import socket,os
 3 
 4 server = socket.socket()
 5 server.bind(('127.0.0.1',9999))
 6 
 7 server.listen()
 8 
 9 while True:
10         conn,addr = server.accept()
11         print("一个新的连接:",addr)
12         while True:
13             print("等待新指令")
14             data = conn.recv(1024)
15             if not data:
16                 print("客户端已经断开")
17                 break
18             print("执行指令:",data)
19             cmd_res = os.popen(data.decode()).read()
20             print("send before")
21             if len(cmd_res) == 0:
22                 cmd_res = "cmd has no output......"
23             conn.send(str(len(cmd_res.encode())).encode())           
         conn.send(cmd_res.encode())
24 print("send done") 25 server.close()

客户端代码如下:

 1 #AUTHOR:FAN
 2 import socket,os
 3 
 4 client = socket.socket()
 5 client.connect(('127.0.0.1',9999))
 6 
 7 while True:
 8         cmd = input(">>:").strip()
 9         if len(cmd) == 0:continue
10         client.send(cmd.encode("utf-8"))
11         cmd_res_size = client.recv(1024)
12         print("命令结果大小:",cmd_res_size)
13         received_data = b''
14         received_data_size = 0
15         while received_data_size < int(cmd_res_size.decode()):
16             data = client.recv(1024)
17             received_data_size+= len(data)这里用len判断长度是因为服务器每次发来的不一定是1024,这个问题一定要注意
18             print(received_data_size)
19             received_data+=data
20         else:
21             print("cmd res receive done")
22             print(received_data.decode())
23 client.close()

客户端运行结果如下:

 1 D:\python35\python.exe D:/python培训/s14/day8/sock_ssh_client.py
 2 >>:dir
 3 命令结果大小: b'852'
 4 852
 5 cmd res receive done
 6  驱动器 D 中的卷是 新加卷
 7  卷的序列号是 7095-8443
 8 
 9  D:\python培训\s14\day8 的目录
10 
11 2016/09/12  23:25    <DIR>          .
12 2016/09/12  23:25    <DIR>          ..
13 2016/09/11  16:54    <DIR>          cc
14 2016/05/12  17:25       181,238,643 jdk8.tar.gz
15 2016/09/10  10:00    <DIR>          lib
16 2016/09/12  23:25               681 sock_ssh_client.py
17 2016/09/12  23:22               608 sock_ssh_server.py
18 2016/09/10  09:34                11 __init__.py
19 2016/09/10  10:26               292 动态导入模块.py
20 2016/09/10  10:06                88 动态导入模块2.py
21 2016/09/11  16:12               102 动态导入模块3.py
22 2016/09/11  16:23               113 动态导入模块4.py
23 2016/09/11  16:45                66 断言.py
24                9 个文件    181,240,604 字节
25                4 个目录 181,201,088,512 可用字节
26 
27 >>:ipconfig
28 命令结果大小: b'1960'
29 1024
30 1960
31 cmd res receive done
32 
33 Windows IP 配置
34 
35 
36 以太网适配器 Bluetooth 网络连接:
37 
38    媒体状态  . . . . . . . . . . . . : 媒体已断开
39    连接特定的 DNS 后缀 . . . . . . . : 
40 
41 以太网适配器 本地连接:
42 
43    媒体状态  . . . . . . . . . . . . : 媒体已断开
44    连接特定的 DNS 后缀 . . . . . . . : 
45 
46 无线局域网适配器 无线网络连接:
47 
48    连接特定的 DNS 后缀 . . . . . . . : DHCP HOST
49    本地链接 IPv6 地址. . . . . . . . : fe80::85b7:6c65:f032:d29%12
50    IPv4 地址 . . . . . . . . . . . . : 192.168.1.103
51    子网掩码  . . . . . . . . . . . . : 255.255.255.0
52    默认网关. . . . . . . . . . . . . : 192.168.1.1
53 
54 以太网适配器 VMware Network Adapter VMnet1:
55 
56    连接特定的 DNS 后缀 . . . . . . . : 
57    本地链接 IPv6 地址. . . . . . . . : fe80::9c7d:99a2:b09b:fa49%15
58    IPv4 地址 . . . . . . . . . . . . : 10.0.10.22
59    子网掩码  . . . . . . . . . . . . : 255.255.255.0
60    默认网关. . . . . . . . . . . . . : 
61 
62 以太网适配器 VMware Network Adapter VMnet8:
63 
64    连接特定的 DNS 后缀 . . . . . . . : 
65    本地链接 IPv6 地址. . . . . . . . : fe80::4d7:4817:776d:6386%16
66    IPv4 地址 . . . . . . . . . . . . : 172.16.1.200
67    子网掩码  . . . . . . . . . . . . : 255.255.255.0
68    默认网关. . . . . . . . . . . . . : 
69 
70 隧道适配器 isatap.DHCP HOST:
71 
72    媒体状态  . . . . . . . . . . . . : 媒体已断开
73    连接特定的 DNS 后缀 . . . . . . . : DHCP HOST
74 
75 隧道适配器 Teredo Tunneling Pseudo-Interface:
76 
77    媒体状态  . . . . . . . . . . . . : 媒体已断开
78    连接特定的 DNS 后缀 . . . . . . . : 
79 
80 隧道适配器 isatap.{AEC4AF99-6E8C-4696-B0CE-1044479986E4}:
81 
82    媒体状态  . . . . . . . . . . . . : 媒体已断开
83    连接特定的 DNS 后缀 . . . . . . . : 
84 
85 隧道适配器 isatap.{E8300FF5-2E5A-4E63-8F58-33282553726B}:
86 
87    媒体状态  . . . . . . . . . . . . : 媒体已断开
88    连接特定的 DNS 后缀 . . . . . . . : 
89 
90 >>:

这个时候命令结果显示正常,并且结果大小也显示正常

但是当把程序放到linux上执行时发现出现问题了,客户端提示错误如下,并且提示错误后程序退出,服务端提示客户端断开连接:

1 >>:ifconfig
2 命令结果大小: b'906ens33     Link encap:Ethernet  HWaddr 00:0c:29:96:2f:bc  \n          inet addr:192.168.1.105  Bcast:192.168.1.255  Mask:255.255.255.0\n          inet6 addr: fe80::b011:2300:ecc8:c70d/64 Scope:Link\n          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1\n          RX packets:814292 errors:0 dropped:0 overruns:0 frame:0\n          TX packets:65371 errors:0 dropped:0 overruns:0 carrier:0\n          collisions:0 txqueuelen:1000 \n          RX bytes:923448604 (923.4 MB)  TX bytes:4865728 (4.8 MB)\n\nlo        Link encap:Local Loopback  \n          inet addr:127.0.0.1  Mask:255.0.0.0\n          inet6 addr: ::1/128 Scope:Host\n          UP LOOPBACK RUNNING  MTU:65536  Metric:1\n          RX packets:1444 errors:0 dropped:0 overruns:0 frame:0\n          TX packets:1444 errors:0 dropped:0 overruns:0 carrier:0\n          collisions:0 txqueuelen:1 \n          RX bytes:115764 (115.7 KB)  TX bytes:115764 (115.7 KB)\n\n'
3 Traceback (most recent call last):
4   File "socket_ssh_client.py", line 16, in <module>
5     while received_data_size < int(cmd_res_size.decode()):
6 ValueError: invalid literal for int() with base 10: '906ens33     Link encap:Ethernet  HWaddr 00:0c:29:96:2f:bc  \n          inet addr:192.168.1.105  Bcast:192.168.1.255  Mask:255.255.255.0\n          inet6 addr: fe80::b011:2300:ecc8:c70d/64 Scope:Link
7 root@python:~/0912#

出现上述错误的原因是因为在服务端的两行代码:

conn.send(str(len(cmd_res.encode())).encode())

conn.send(cmd_res.encode())

这两句代码连着发导致了粘包:

两次连着send缓冲区会将两次的数据合并为一条发送给客户端从而导致数据粘在一起

解决方法:

服务端代码:

 1 #AUTHOR:FAN
 2 import socket,os
 3 
 4 server = socket.socket()
 5 server.bind(('127.0.0.1',9999))
 6 
 7 server.listen()
 8 
 9 while True:
10         conn,addr = server.accept()
11         print("一个新的连接:",addr)
12         while True:
13             print("等待新指令")
14             data = conn.recv(1024)
15             if not data:
16                 print("客户端已经断开")
17                 break
18             print("执行指令:",data)
19             cmd_res = os.popen(data.decode()).read()
20             print("send before")
21             if len(cmd_res) == 0:
22                 cmd_res = "cmd has no output......"
23             conn.send(str(len(cmd_res.encode())).encode())
24             client_ack = conn.recv(1024)
25             print("来自客户端的确认:",client_ack.decode())
26             conn.send(cmd_res.encode())
27             print("send done")
28 server.close()

客户端:

 1 #AUTHOR:FAN
 2 import socket,os
 3 
 4 client = socket.socket()
 5 
 6 client.connect(('127.0.0.1',9999))
 7 #client.connect(('192.168.1.105',9999))
 8 
 9 while True:
10         cmd = input(">>:").strip()
11         if len(cmd) == 0:continue
12         client.send(cmd.encode("utf-8"))
13         cmd_res_size = client.recv(1024)
14         client.send("准备好接收数据了".encode())
15         print("命令结果大小:",cmd_res_size)
16         received_data = b''
17         received_data_size = 0
18         while received_data_size < int(cmd_res_size.decode()):
19             data = client.recv(1024)
20             received_data_size+= len(data)
21             print(received_data_size)
22             received_data+=data
23         else:
24             print("cmd res receive done")
25             print(received_data.decode())
26 client.close()

这样就完美的解决了粘包的问题

4、 SocketServer模块

SocketServer内部使用IO多路复用以及“多线程”和“多进程”,从而实现并发处理多个客户端请求的Socket服务端,即:每个客户端请求连接到服务器时,Socket服务端都会在服务器上创建一个线程或进程专门负责处理当前客户端的所有请求

关于SocketServer的使用:

a. 必须自己创建一个请求处理类,并且这个类要继承BaseRequestHandler并且还要重写父类里的handle()

b. 必须实例化TCPServer,并且传递server ip和上面创建的请求处理类给这个TCPServer

c. server.handle_request()只处理一个请求

server.server_forever()处理多个请求,永远执行

d. 最后关闭socket

下面是一个socketserver的一个例子:

服务端:

 1 #AUTHOR:FAN
 2 
 3 import socketserver
 4 #对应上面所说的自己创建一个请求处理类并继承BaseRequestHandler
 5 class MyTCPHandler(socketserver.BaseRequestHandler):
 6 
 7 #对应上面的重写父类里的handle(),切记所有的交互都在handle()里面切记
 8 def handle(self):  
 9         while True:
10             try:
11                 self.data = self.request.recv(1024).strip()
12                 print("{} wrote:".format(self.client_address[0]))
13                 print(self.data)
14                 self.request.sendall(self.data.upper())
15             except ConnectionResetError as e:
16                 print(e)
17                 break
18 if __name__ == "__main__":
19         HOST,PORT = '127.0.0.1',9999
20 #这里对应上面的实例化TCPServer,并传递server ip和上面创建的请求处理类,也就是MyTCPHandler
21 server = socketserver.TCPServer((HOST,PORT),MyTCPHandler)
22 server.serve_forever()

 

所有的努力都值得期许,每一份梦想都应该灌溉!
目录
相关文章
|
8天前
|
安全 Java 数据处理
Python网络编程基础(Socket编程)多线程/多进程服务器编程
【4月更文挑战第11天】在网络编程中,随着客户端数量的增加,服务器的处理能力成为了一个重要的考量因素。为了处理多个客户端的并发请求,我们通常需要采用多线程或多进程的方式。在本章中,我们将探讨多线程/多进程服务器编程的概念,并通过一个多线程服务器的示例来演示其实现。
|
8天前
|
程序员 开发者 Python
Python网络编程基础(Socket编程) 错误处理和异常处理的最佳实践
【4月更文挑战第11天】在网络编程中,错误处理和异常管理不仅是为了程序的健壮性,也是为了提供清晰的用户反馈以及优雅的故障恢复。在前面的章节中,我们讨论了如何使用`try-except`语句来处理网络错误。现在,我们将深入探讨错误处理和异常处理的最佳实践。
|
13天前
|
网络协议 程序员 Python
pythonTCP客户端编程创建Socket对象
【4月更文挑战第6天】本教程介绍了TCP客户端如何创建Socket对象。Socket作为网络通信的基础单元,包含协议、IP地址和端口等信息。在TCP/IP中,Socket分为流式(TCP)、数据报(UDP)和原始套接字。以Python为例,创建TCP Socket对象需调用`socket.socket(AF_INET, SOCK_STREAM)`。为确保健壮性,应使用异常处理处理可能的`socket.error`。学习本教程将帮助你掌握TCP客户端创建Socket对象的技能。
|
1月前
|
网络协议 安全 API
计算机网络之Socket编程
计算机网络之Socket编程
|
2月前
|
网络协议 安全 开发者
Python 中的 Socket 编程
Python 中的 Socket 编程
44 4
|
3月前
socket编程之回声服务器函数的陷阱
由connect函数使用不当导致的小错误 话不多说先看代码:
25 0
|
3月前
|
API C++
socket编程之常用api介绍与socket、select、poll、epoll高并发服务器模型代码实现(1)
前言   本文旨在学习socket网络编程这一块的内容,epoll是重中之重,后续文章写reactor模型是建立在epoll之上的。
34 0
|
3月前
|
监控 安全 Linux
socket编程之常用api介绍与socket、select、poll、epoll高并发服务器模型代码实现(3)
高并发服务器模型-poll poll介绍   poll跟select类似, 监控多路IO, 但poll不能跨平台。其实poll就是把select三个文件描述符集合变成一个集合了。
35 0
|
7天前
|
网络协议 Java API
Python网络编程基础(Socket编程)Twisted框架简介
【4月更文挑战第12天】在网络编程的实践中,除了使用基本的Socket API之外,还有许多高级的网络编程库可以帮助我们更高效地构建复杂和健壮的网络应用。这些库通常提供了异步IO、事件驱动、协议实现等高级功能,使得开发者能够专注于业务逻辑的实现,而不用过多关注底层的网络细节。
|
11天前
|
Python
Python网络编程基础(Socket编程)UDP服务器编程
【4月更文挑战第8天】Python UDP服务器编程使用socket库创建UDP套接字,绑定到特定地址(如localhost:8000),通过`recvfrom`接收客户端数据报,显示数据长度、地址和内容。无连接的UDP协议使得服务器无法主动发送数据,通常需应用层实现请求-响应机制。当完成时,用`close`关闭套接字。