zoukankan      html  css  js  c++  java
  • day08 多线程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 = 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

     

  • 相关阅读:
    ASP.NET 表单验证 Part.1(理解表单验证)
    Silverlight 简介 Part.3(设计 Siverlight 页面)
    ASP.NET 成员资格 Part.3(LoginStatus、LoginView、PasswordRecovery)
    ASP.NET 网站部署 Part.1(安装IIS、复制文件部署网站)
    ASP.NET Dynamic Data Part.1(创建动态数据应用程序)
    ASP.NET 安全模型 Part.2(SSL)
    ASP.NET MVC Part.2(扩展基本的 MVC 应用程序)
    ASP.NET 网站部署 Part.2(使用 Web 部署)
    开发高级 Web 部件
    创建 Web 部件(WebPart 类、简单的 Web 部件)
  • 原文地址:https://www.cnblogs.com/yangever/p/5876115.html
Copyright © 2011-2022 走看看