首页 > 代码库 > day8---多线程socket 编程,tcp粘包处理

day8---多线程socket 编程,tcp粘包处理

复习下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 = http://www.mamicode.com/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 = http://www.mamicode.com/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外的test10 import importlib11 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             break17         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         continue14     client.send(cmd)15     cmd_size = int(client.recv(1024).decode())16     print(cmd_size)17     recov_size = 018     recov_data = http://www.mamicode.com/b‘‘"收完了,大小",recov_size)27         print(recov_data.decode())28 client.close()

技术分享

 

 

 上面的代码 在linux 发送 会出现粘包   客户端收到的是两个数据

 

终极解决办法:
 
服务端添加接收过程:

技术分享

客户端加发送过程:

技术分享

实践:
ssh_server:
#!/usr/bin/env python3import socket,osip_address = ‘192.168.81.133‘port = 8888bind_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         continue12     client.send(cmd)13     cmd_size = int(client.recv(1024).decode())14     print(cmd_size)15     client.send("收到大小".encode(‘utf-8‘))16     recov_size = 017     recov_data = http://www.mamicode.com/b‘‘"收完了,大小",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             break17         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_size23           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         continue12     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 = 022       m = hashlib.md5()23       while recov_file_size < total_file_size:24           if total_file_size - recov_file_size > 1024:25              size = 102426           else:27              size =  total_file_size - recov_file_size28              print("最后收",size)29           #print("收数据...")30           data = http://www.mamicode.com/client.recv(size)"收完了,大小",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 = http://www.mamicode.com/self.request.recv(1024).strip()"来自",self.client_address) 8                 print(self.data) 9                 self.request.send(self.data.upper())10             except ConnectionResetError as e:11                 print("客户端退出",e)12                 break13 if __name__ == "__main__":14     HOST,PORT="localhost",999915     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 = http://www.mamicode.com/client.recv(10240)"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

 技术分享

 

day8---多线程socket 编程,tcp粘包处理