复习下socket 编程的步骤:
服务端:
1 声明socket 实例
server = socket.socket() #括号里不写 默认地址簇使用AF_INET 即 IPv4 默认type 为 sock.SOCK_STREAM 即 TCP/IP 协议
2 绑定IP地址和端口
server.bind(('localhost',9999)) #ip地址和端口 元组形式 ,端口为整数形式
3 开始监听
server.listen()
4 进入阻塞状态,等待连接进入
######## 从这里开始可以加入循环,循环不同的连接 ,这样就可以接受不同的客户端的连接了#####
#while True:
# 获取客户端ip地址和端口 开启一个连接实例
############## 从这里开始循环,这样就可以循环收到客户端数据,但是只收这一个客户端 ###########
##客户端一断开,conn收到的就全为空数据,这样就进入了一个死循环,所以需要一个if 判断
#while True:
5 收数据
data = conn.recv(1024) #接收最大数据量, 官方建议最大8192 写整数据
# 这里加入if 判断
# if not data:
# break
print(data)
6 可以往回发送数据
conn.send(data.upper()) #可以发送任何数据,这里把收到的变为大写
7 server.close()
客户端:
1 声明socket 实例:
client= socket.socket()
2 连接服务端:
client.connect(('server_ip',9999)) #连接服务端的ip及端口 元组形式 ,端口为整数形式
3 发数据
client.send(data) #发送数据,任意数据
# 4 收取服务端返回的数据
# recv_data = client.recv(1024)
动态导入模块:
1 实践:
2 1 lib下的aa
3 def test():
4 print("hehe")
5 class C(object):
6 name = "yang"
7 def __init__(self):
8 self.name = 'shenyang' 9 lib外的test
10 import importlib
11 dd = importlib.import_module('lib.aa')
12 dd.test()
13 print(dd.C().name)
运行:
断言:
判断类型
正确判断
判断错误
和 if 判断 一样 但是更简洁,一句话就OK
继续socket 编程:
客户端虽然写的是收1024 但是不一定收到1024 只代表最多收到1024 即使服务端返回100个字节 也收
客户端收完数据,收多少 要服务端先发一个要发多少的数据,客户端收到后,开始接收数据,直到收到指定大小的数据为止,不再接收
实践:
服务端
1 #!/usr/bin/env python3
2 # Author: Shen Yang
3 import socket,os
4 ip_address = '192.168.16.10'
5 port = 8888
6 bind_address = (ip_address,port)
7 server = socket.socket()
8 server.bind(bind_address)
9 server.listen()
10 while True:
11 conn,addr = server.accept()
12 while True:
13 data = conn.recv(1024).decode()
14 if not data:
15 print("丢失连接")
16 break
17 print("这是来自",addr,data)
18 cmd_res = os.popen(data).read()
19 conn.send(str(len(cmd_res.encode('utf-8'))).encode('utf-8'))
20 conn.send(cmd_res.encode('utf-8'))
21 server.close()
客户端
1 #!/usr/bin/env python3
2 # Author: Shen Yang
3 import socket
4 ip_address = '192.168.16.10'
5 # ip_address = '192.168.81.133'
6 port = 8888
7 conn_address = (ip_address,port)
8 client = socket.socket()
9 client.connect(conn_address)
10 while True:
11 cmd = input(":> ").encode('utf-8')
12 if len(cmd) == 0:
13 continue
14 client.send(cmd)
15 cmd_size = int(client.recv(1024).decode())
16 print(cmd_size)
17 recov_size = 0
18 recov_data = b''
19 while recov_size < cmd_size:
20 data = client.recv(1024)
21 #print(data)
22 recov_size += len(data)
23 #print(recov_size)
24 recov_data += data
25 else:
26 print("收完了,大小",recov_size)
27 print(recov_data.decode())
28 client.close()
上面的代码 在linux 发送 会出现粘包 客户端收到的是两个数据
终极解决办法:
服务端添加接收过程:
客户端加发送过程:
实践:
ssh_server:
#!/usr/bin/env python3
import socket,os
ip_address = '192.168.81.133'
port = 8888
bind_address = (ip_address,port)
server = socket.socket()
server.bind(bind_address)
server.listen()
while True:
conn,addr = server.accept()
while True:
data = conn.recv(1024).decode()
if not data:
print("丢失连接")
break
print("这是来自",addr,data)
cmd_res = os.popen(data).read()
conn.send(str(len(cmd_res.encode('utf-8'))).encode('utf-8'))
ack = conn.recv(1024).decode()
print(ack)
conn.send(cmd_res.encode('utf-8'))
server.close()
ssh_client
1 #!/usr/bin/env python3
2 import socket
3 ip_address = '192.168.81.133'
4 port = 8888
5 conn_address = (ip_address,port)
6 client = socket.socket()
7 client.connect(conn_address)
8 while True:
9 cmd = input(":> ").encode('utf-8')
10 if len(cmd) == 0:
11 continue
12 client.send(cmd)
13 cmd_size = int(client.recv(1024).decode())
14 print(cmd_size)
15 client.send("收到大小".encode('utf-8'))
16 recov_size = 0
17 recov_data = b''
18 while recov_size < cmd_size:
19 data = client.recv(1024)
20 #print(data)
21 recov_size += len(data)
22 #print(recov_size)
23 recov_data += data
24 else:
25 print("收完了,大小",recov_size)
26 print(recov_data.decode())
27 client.close()
传文件:
server 端逻辑:
使用 os.stat 获取文件详细信息,发送给客户端
另外一个解决粘包的方法:
只收指定大小的数据 永远不可能粘包
实践:
ftp_server.py
1 #!/usr/bin/env python3
2 import socket,os,hashlib
3 ip_address = '192.168.81.133'
4 port = 8888
5 bind_address = (ip_address,port)
6 server = socket.socket()
7 server.bind(bind_address)
8 server.listen()
9 while True:
10 conn,addr = server.accept()
11 while True:
12 print("等待新链接")
13 data = conn.recv(1024)
14 if not data:
15 print("丢失连接")
16 break
17 print("这是来自",addr,data)
18 cmd,file_name = data.decode().split()
19 if os.path.isfile(file_name):
20 m = hashlib.md5()
21 f = open(file_name,'rb')
22 file_size = os.stat(file_name).st_size
23 conn.send(str(file_size).encode())
24 ack = conn.recv(1024).decode()
25 print("确认:",ack)
26 for line in f:
27 m.update(line)
28 print("sending...")
29 conn.send(line)
30 f.close()
31 conn.send(m.hexdigest().encode())
32 print("发送完毕")
33 server.close()
ftp_client.py
1 #!/usr/bin/env python3
2 import socket,hashlib
3 ip_address = '192.168.81.133'
4 port = 8888
5 conn_address = (ip_address,port)
6 client = socket.socket()
7 client.connect(conn_address)
8 while True:
9 cmd = input("input your cmd:> ").strip()
10 if len(cmd) == 0:
11 continue
12 if cmd.startswith("get"):
13 client.send(cmd.encode('utf-8'))
14 file_name = cmd.split()[1]
15 server_response = client.recv(1024)
16 print("收到回应:",server_response)
17 client.send("收到".encode('utf-8'))
18 total_file_size = int(server_response.decode())
19 print("总的",total_file_size)
20 f = open(file_name + '.new','wb')
21 recov_file_size = 0
22 m = hashlib.md5()
23 while recov_file_size < total_file_size:
24 if total_file_size - recov_file_size > 1024:
25 size = 1024
26 else:
27 size = total_file_size - recov_file_size
28 print("最后收",size)
29 #print("收数据...")
30 data = client.recv(size)
31 m.update(data)
32 recov_file_size += len(data)
33 f.write(data)
34 else:
35 print("收完了,大小",recov_file_size,total_file_size)
36 f.close()
37 print("服务器端发送的原MD5",client.recv(1024).decode())
38 print("自己算出来的最终MD5",m.hexdigest())
39 client.close()
开始学习SocketSever
SocketSever 是对socket 的再封装
最常用的是 TCP 和 UDP
继承关系:
创建socket server 步骤:
下面是重写的handle:
跟客户端所有的交互都是在handle 里处理的
每一个客户端请求都是实例化一个handle
继续修改handle
增加循环:
并增加判断客户端退出:
实践:
server:
1 import socketserver
2 class MyTCPHandler(socketserver.BaseRequestHandler):
3 def handle(self):
4 while True:
5 try:
6 self.data = self.request.recv(1024).strip()
7 print("来自",self.client_address)
8 print(self.data)
9 self.request.send(self.data.upper())
10 except ConnectionResetError as e:
11 print("客户端退出",e)
12 break
13 if __name__ == "__main__":
14 HOST,PORT="localhost",9999
15 server = socketserver.TCPServer((HOST,PORT),MyTCPHandler)
16 server.serve_forever()
client:
1 import socket
2 client = socket.socket() #声明socket类型,同时生成socket连接对象
3 #client.connect(('192.168.16.200',9999))
4 client.connect(('localhost',9999))
5 while True:
6 msg = input(">>:").strip()
7 if len(msg) == 0:continue
8 client.send(msg.encode("utf-8"))
9 data = client.recv(10240)
10 print("recv:",data.decode())
11 client.close()
上面的并不支持多并发
到现在为止,只是写了一个一对一的服务器
下面写入多并发:
只改一个地方:
1 使用多线程 来实现
2 使用多进程实现: (windos 下不行,不支持,但是linux 绝得好使)
介绍 socketserver的一些方法:
1 返回文件描述符 (系统自己调用,我们一般用不到)
2 处理单个请求,一般也用不到
3 一直收请求,直到收到一个明确的shutdown (每0.5秒检测是否给我发了shutdown 的信号)
收到shutdown 后调用 service_action() 清理垃圾僵尸进程
4 告诉 server_forever() 关闭
5 地址的重用,不需要等待tcp断开
普通的sock server
server = socket.socket() #获得socket实例
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #重用地址
请求的三个过程,都可以自定义:
1 setup
2 handle
3 finish