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

服务端显示:

  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''
  4. 1024
  5. 1960
  6. cmd res receive done
  7. >>:dir
  8. 命令结果大小: b''
  9. 852
  10. cmd res receive done
  11. >>:ipconfig /all
  12. 命令结果大小: b''
  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''
  4. 11
  5. cmd res receive done
  6. >>:ifconfig
  7. 命令结果大小: b''
  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''
  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''
  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''
  29. 1024
  30. 1960
  31. cmd res receive done
  32.  
  33. Windows IP 配置
  34.  
  35. 以太网适配器 Bluetooth 网络连接:
  36.  
  37. 媒体状态 . . . . . . . . . . . . : 媒体已断开
  38. 连接特定的 DNS 后缀 . . . . . . . :
  39.  
  40. 以太网适配器 本地连接:
  41.  
  42. 媒体状态 . . . . . . . . . . . . : 媒体已断开
  43. 连接特定的 DNS 后缀 . . . . . . . :
  44.  
  45. 无线局域网适配器 无线网络连接:
  46.  
  47. 连接特定的 DNS 后缀 . . . . . . . : DHCP HOST
  48. 本地链接 IPv6 地址. . . . . . . . : fe80::85b7:6c65:f032:d29%12
  49. IPv4 地址 . . . . . . . . . . . . : 192.168.1.103
  50. 子网掩码 . . . . . . . . . . . . : 255.255.255.0
  51. 默认网关. . . . . . . . . . . . . : 192.168.1.1
  52.  
  53. 以太网适配器 VMware Network Adapter VMnet1:
  54.  
  55. 连接特定的 DNS 后缀 . . . . . . . :
  56. 本地链接 IPv6 地址. . . . . . . . : fe80::9c7d:99a2:b09b:fa49%15
  57. IPv4 地址 . . . . . . . . . . . . : 10.0.10.22
  58. 子网掩码 . . . . . . . . . . . . : 255.255.255.0
  59. 默认网关. . . . . . . . . . . . . :
  60.  
  61. 以太网适配器 VMware Network Adapter VMnet8:
  62.  
  63. 连接特定的 DNS 后缀 . . . . . . . :
  64. 本地链接 IPv6 地址. . . . . . . . : fe80::4d7:4817:776d:6386%16
  65. IPv4 地址 . . . . . . . . . . . . : 172.16.1.200
  66. 子网掩码 . . . . . . . . . . . . : 255.255.255.0
  67. 默认网关. . . . . . . . . . . . . :
  68.  
  69. 隧道适配器 isatap.DHCP HOST:
  70.  
  71. 媒体状态 . . . . . . . . . . . . : 媒体已断开
  72. 连接特定的 DNS 后缀 . . . . . . . : DHCP HOST
  73.  
  74. 隧道适配器 Teredo Tunneling Pseudo-Interface:
  75.  
  76. 媒体状态 . . . . . . . . . . . . : 媒体已断开
  77. 连接特定的 DNS 后缀 . . . . . . . :
  78.  
  79. 隧道适配器 isatap.{AEC4AF99-6E8C-4696-B0CE-1044479986E4}:
  80.  
  81. 媒体状态 . . . . . . . . . . . . : 媒体已断开
  82. 连接特定的 DNS 后缀 . . . . . . . :
  83.  
  84. 隧道适配器 isatap.{E8300FF5-2E5A-4E63-8F58-33282553726B}:
  85.  
  86. 媒体状态 . . . . . . . . . . . . : 媒体已断开
  87. 连接特定的 DNS 后缀 . . . . . . . :
  88.  
  89. >>:

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

但是当把程序放到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()

socket编程进阶的更多相关文章

  1. Python之路,Day8 - Socket编程进阶

    Python之路,Day8 - Socket编程进阶   本节内容: Socket语法及相关 SocketServer实现多并发 Socket语法及相关 socket概念 socket本质上就是在2台 ...

  2. PHP Socket 编程进阶指南

    学习准备 Linux 或者 Mac 环境: 安装有 Sockets 扩展: 了解 TCP/IP 协议. socket函数只是PHP扩展的一部分,编译PHP时必须在配置中添加 --enable-sock ...

  3. Python 8 - Socket编程进阶

    本节内容:    1.Socket语法及相关    2.SocketServer实现多并发 Socket语法及相关 socket 概念 socket本质上就是在2台网络互通的电脑之间架设一个通道,两台 ...

  4. Python自动化 【第八篇】:Python基础-Socket编程进阶

    本节内容: Socket语法及相关 SocketServer实现多并发 1. Socket语法及相关 sk = socket.socket(socket.AF_INET,socket.SOCK_STR ...

  5. Python学习笔记——进阶篇【第八周】———进程、线程、协程篇(Socket编程进阶&多线程、多进程)

    本节内容: 异常处理 Socket语法及相关 SocketServer实现多并发 进程.线程介绍 threading实例 线程锁.GIL.Event.信号量 生产者消费者模型 红绿灯.吃包子实例 mu ...

  6. Python之socket编程进阶版

    1.socket之简单的ssh功能 2.socket之简单的ftp服务器 3.socketserver的用法 4.socketserver的多并发的实现 1.socket实现ssh服务 1.1我们现在 ...

  7. Day8 - Python网络编程 Socket编程

    Python之路,Day8 - Socket编程进阶   本节内容: Socket语法及相关 SocketServer实现多并发 Socket语法及相关 socket概念 socket本质上就是在2台 ...

  8. Python学习笔记——基础篇【第七周】———FTP作业(面向对象编程进阶 & Socket编程基础)

    FTP作业 本节内容: 面向对象高级语法部分 Socket开发基础 作业:开发一个支持多用户在线的FTP程序 面向对象高级语法部分 参考:http://www.cnblogs.com/wupeiqi/ ...

  9. python进阶---Python中的socket编程

    初识socket编程 一.前言 socket基于C\S架构(客户端\服务端)的编程模型,在Python中是以socket模块存在的. Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是 ...

随机推荐

  1. 算法系列:FFT 002

    转载自http://blog.jobbole.com/58246/ 快速傅里叶变换(Fast Fourier Transform)是信号处理与数据分析领域里最重要的算法之一.没有正规计算机科学课程背景 ...

  2. 注解:【基于主键的】Hibernate1->1关联

    参考:http://blog.sina.com.cn/s/blog_674b23220100hjb5.html Husband.java package org.crazyit.app.domain; ...

  3. 通过VS创建简单的WCF服务

    http://www.cnblogs.com/artech/archive/2007/09/15/893838.html http://www.topwcftutorials.net/2013/09/ ...

  4. 移动WEB前端开发资源整合

    meta篇 1.视窗宽度 <meta name="viewport" content="width=device-width,initial-scale=1.0,m ...

  5. Ecplise真机调试(Android)

    手机连电脑: 利用豌豆荚等工具安装驱动: 安卓手机打开开发人员选项,usb调试: 可以在cmd中执行sdk文件夹中的adb查看是否连接成功://adb devices 可将adb.exe所在文件路径加 ...

  6. nodejs review-01

    lesson lesson-code 05 Run your first web server 使用curl //指定方法;显示header信息 curl -X GET -i localhost:30 ...

  7. 【BZOJ1076】[SCOI2008]奖励关 状压DP+期望

    [BZOJ1076][SCOI2008]奖励关 Description 你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关.在这个奖励关里,系统将依次随机抛出k次宝物,每次你都可以选择吃或者不吃(必须 ...

  8. JavaScript 入门教程四 语言基础【3】

    一.数据类型介绍: String 1.是使用 Unicode 字符组成的字符序列.所以可以容纳各种语言. 2.字符串可以使用双引号(“”)或者单引号('')表示,但必须成对出现. 3.转移序列使用 \ ...

  9. Ruby-随机数

    --随机0-1 rand --随机一个范围 rand(1..100)

  10. javascript 数字进制转换

    //十进制转其他 var x=110; alert(x); alert(x.toString(8)); alert(x.toString(32)); alert(x.toString(16)); // ...