zoukankan      html  css  js  c++  java
  • 网络编程

    osi模型

    学习socket需要了解一些网络知识,其中osi模型为基础~~

    互联网协议按照功能不同分为osi七层或tcp/ip五层或tcp/ip四层

    我们将应用层,表示层,会话层并作应用层,从tcp/ip五层协议的角度来阐述每层的由来与功能,搞清楚了每层的主要协议

    理解网络之中的TCP通信之三次握手四次挥手

    交互

    通过使用python的socket模块实现简单的客户端与服务端的交互

    #服务端,我们把网络交互看作是打电话
    import socket
    #买手机
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#socket.AF_INET基于网络的套接字,sock_stream流式套接字 tcp,sock_SOCK_DGRAM 数据报协议 utp
    
    #插卡
    phone.bind(('127.0.0.1',8080))#唯一标识软件up+端口
    
    #开机
    phone.listen(5)#监听,由于单线程会有1个正常通信,最大5个半连接
    
    #等电话连接
    while True:
        conn,client_addr = phone.accept()#接收
        print(conn,client_addr)
        while True:
            try:#当客户端当方面断开连接时为避免服务端异常报错使用异常处理
                #基于建立的连接,收发消息
                client_data = conn.recv(1024)
                print(client_data)
                if not client_data:break#不能为空,收到空服务端不会返回消息
                conn.send(client_data.upper())
            except Exception:
                break
    
    #挂电话
        conn.close()
    
    #关机
    phone.close()
    #客户端
    import socket
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#sock_stream流式套接字 tcp,sock_SOCK_DGRAM 数据报协议 utp
    phone.connect(('127.0.0.1',8080))
    
    while True:
        msg = input('>>>').strip()
        if  not msg:continue
        phone.send(msg.encode('utf-8'))
        server_data = phone.recv(1024)
        print(server_data.decode('utf-8'))
    
    phone.close()

    解决粘包问题

    由于tcp协议是流式协议,所以连续传输会出现数据粘连的情况,要想准确接收想收到的数据,需要在发送数据前发送个报文头,来标识数据的长度。

    打包模块struck

    该模块可以把一个类型,如数字,转成固定长度的bytes

    >>> struct.pack('i',1111111111111)

     

    解决粘包的流程为:

    客户端订制报头

            head_dic={'cmd':cmd,'filename':os.path.basename(filename),'filesize':filesize}#报头包括data的一些信息
            head_json=json.dumps(head_dic)#为了能够传输需转化为json对象
            head_json_bytes=bytes(head_json,encoding=self.coding)#转化为字节

    客户端将报文头打包成4个字节
        head_struct=struct.pack('i',len(head_json_bytes))
    客户端发送报文头长度
        self.socket.send(head_struct)
    客户端发送报文头
        self.socket.send(head_json_bytes)
    客户端发送数据
            with open(filename,'rb') as f:
                for line in f:
                    self.socket.send(line)
    服务端接收头长度4
        head_struct = self.conn.recv(4)
    服务端解报文头包得到字典中需要的key,真是发送数据的长度
        head_len = struct.unpack('i', head_struct)[0]
    服务端接收真实数据长度,并以utf-8解码,解json
        head_json = self.conn.recv(head_len).decode('utf-8')
        head_dic = json.loads(head_json)

    socketserver实现并发

    服务端代码

    #服务端固定格式
    class FtpServer(socketserver.BaseRequestHandler):
        def handle(self):#链路循环每来一个链接变产生一个链接对象
            pass
    
    ftpserver=socketserver.ThreadingTCPServer(('127.0.0.1',8080),FtpServer)
    ftpserver.serve_forever()
    import socketserver
    class FtpServer(socketserver.BaseRequestHandler):
        coding = 'utf-8'
        def handle(self):
            while True:
                res = self.request.recv(1024)
                res = res.decode('utf-8')
                res = json.loads(res)
                if hasattr(self,res['cmd']):#可在传输中定义要执行的功能
                    func = getattr(self, res['cmd'])
                    func(res)
                print(res)
    
        def login(self,res):
            '''
            登录接口
            '''
            pass
    
        def register(self,res):
            '''
            注册接口
            :param res:
            :return:
            '''
            pass
    
    ftpserver = socketserver.ThreadingTCPServer(('127.0.0.1',8088),FtpServer)
    ftpserver.serve_forever()

    基于UDP的套接字

    
    
    1. TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
    2. UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
    3. tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头,实验略
    
    
    #基于udp的server端
    from socket import *
    udp_server = socket(AF_INET,SOCK_DGRAM)#数据报
    udp_server.bind(('127.0.0.1',8080))
    #不需要监听连接listen
    #不需要链接循环accpt
    while True:
    #不会产生粘包,当一条消息没有接收完整,就会丢失 data,client_addr
    = udp_server.recvfrom(1024)#最大接受512字节 例如:dns传输,qq消息传输 print(data,client_addr) udp_server.sendto(data.upper(),client_addr) #发消息不会等客户端确认,便会删除自身缓存中的内容
    
    
    #基于udp的client端
    from socket import *
    client_socket = socket(AF_INET,SOCK_DGRAM)
    #不需要conn服务端
    while True:
        msg = input('>>>')
        client_socket.sendto(msg.encode('utf-8'),('127.0.0.1',8080))
        data,server_addr = client_socket.recvfrom(1024)
        print(data.decode('utf-8'))

     利用socket实现并发

    import socketserver
    
    #并发udp客户端
    class MyUdpHandler(socketserver.BaseRequestHandler):
        def handle(self):
            print(self.request)
            self.request[1].sendto(self.request[0].upper(),self.client_address)#客户端地址
    
    
    
    if __name__ == '__main__':
        s = socketserver.ThreadingUDPServer(('127.0.0.1',8080),MyUdpHandler)
        s.serve_forever()
    #客户端
    from socket import *
    client_socket = socket(AF_INET,SOCK_DGRAM)
    #不需要conn服务端
    while True:
        msg = input('>>>')
        client_socket.sendto(msg.encode('utf-8'),('127.0.0.1',8080))
        data,server_addr = client_socket.recvfrom(1024)
        print(data.decode('utf-8'))
    
    

    paramiko模块

    1、paramiko是一个用于做远程控制的模块,使用该模块可以对远程服务器进行命令或文件操作,值得一说的是,fabric和ansible内部的远程管理就是使用的paramiko来现实。

    
    
    下载:pip3 install paramiko #在python3中

    pycrypto,由于 paramiko 模块内部依赖pycrypto,所以先下载安装pycrypto #在python2中 pip3 install pycrypto pip3 install paramiko 注:如果在安装pycrypto2.0.1时发生如下错误 command 'gcc' failed with exit status 1... 可能是缺少python-dev安装包导致 如果gcc没有安装,请事先安装gcc 在python2中
    
    

    2、用于连接远程服务器并执行基本命令

    基于用户名密码连接

    import paramiko
    
    # 创建SSH对象
    ssh = paramiko.SSHClient()
    # 允许连接不在know_hosts文件中的主机
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    # 连接服务器
    ssh.connect(hostname='120.92.84.249', port=22, username='root', password='xxx')
    
    # 执行命令
    stdin, stdout, stderr = ssh.exec_command('df')
    # 获取命令结果
    result = stdout.read()
    print(result.decode('utf-8'))
    # 关闭连接
    ssh.close()
    import paramiko
    
    transport = paramiko.Transport(('120.92.84.249', 22))
    transport.connect(username='root', password='xxx')
    
    ssh = paramiko.SSHClient()
    ssh._transport = transport
    
    stdin, stdout, stderr = ssh.exec_command('df')
    res=stdout.read()
    print(res.decode('utf-8'))
    
    transport.close()
    
    SSHClient 封装 Transport

    3 基于公钥密钥连接

    客户端文件名:id_rsa

    服务端必须有文件名:authorized_keys(在用ssh-keygen时,必须制作一个authorized_keys,可以用ssh-copy-id来制作)

     
    import paramiko
    
    private_key = paramiko.RSAKey.from_private_key_file('/tmp/id_rsa')
    
    # 创建SSH对象
    ssh = paramiko.SSHClient()
    # 允许连接不在know_hosts文件中的主机
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    # 连接服务器
    ssh.connect(hostname='120.92.84.249', port=22, username='root', pkey=private_key)
    
    # 执行命令
    stdin, stdout, stderr = ssh.exec_command('df')
    # 获取命令结果
    result = stdout.read()
    print(result.decode('utf-8'))
    # 关闭连接
    ssh.close()
    
    
    import paramiko
    
    private_key = paramiko.RSAKey.from_private_key_file('/tmp/id_rsa')
    
    transport = paramiko.Transport(('120.92.84.249', 22))
    transport.connect(username='root', pkey=private_key)
    
    ssh = paramiko.SSHClient()
    ssh._transport = transport
    
    stdin, stdout, stderr = ssh.exec_command('df')
    result=stdout.read()
    print(result.decode('utf-8'))
    
    transport.close()
    
    SSHClient 封装 Transport

    SFTPClient

    基于用户名密码上传下载

    import paramiko
     
    transport = paramiko.Transport(('120.92.84.249',22))
    transport.connect(username='root',password='xxx')
     
    sftp = paramiko.SFTPClient.from_transport(transport)
    # 将location.py 上传至服务器 /tmp/test.py
    sftp.put('/tmp/id_rsa', '/etc/test.rsa')
    # 将remove_path 下载到本地 local_path
    sftp.get('remove_path', 'local_path')
     
    transport.close()
    import paramiko
    
    private_key = paramiko.RSAKey.from_private_key_file('/tmp/id_rsa')
    
    transport = paramiko.Transport(('120.92.84.249', 22))
    transport.connect(username='root', pkey=private_key )
    
    sftp = paramiko.SFTPClient.from_transport(transport)
    # 将location.py 上传至服务器 /tmp/test.py
    sftp.put('/tmp/id_rsa', '/tmp/a.txt')
    # 将remove_path 下载到本地 local_path
    sftp.get('remove_path', 'local_path')
    
    transport.close()
     
  • 相关阅读:
    【SignalR学习系列】3. SignalR实时高刷新率程序
    【SignalR学习系列】4. SignalR广播程序
    【SignalR学习系列】5. SignalR WPF程序
    python gb2312 转换为 utf-8
    爬虫 需要什么样的 CPU,内存 和带宽
    TypeError: sequence item 0: expected string, Tag found
    MySQL 数据的 截取,数据清洗
    MySQL (1366, "Incorrect string value: '\xF0\x9F\x8E\xAC\xE5\x89...' for column 'description' at row 1")
    微博爬虫 ----- 微博发布时间清洗
    ReferenceError: weakly-referenced object no longer exists Python kafka
  • 原文地址:https://www.cnblogs.com/kunixiwa/p/7401821.html
Copyright © 2011-2022 走看看