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()
     
  • 相关阅读:
    Linq聚合操作之Aggregate,Count,Sum,Distinct源码分析
    Linq分区操作之Skip,SkipWhile,Take,TakeWhile源码分析
    Linq生成操作之DefautIfEmpty,Empty,Range,Repeat源码分析
    Linq基础操作之Select,Where,OrderBy,ThenBy源码分析
    PAT 1152 Google Recruitment
    PAT 1092 To Buy or Not to Buy
    PAT 1081 Rational Sum
    PAT 1084 Broken Keyboard
    PAT 1077 Kuchiguse
    PAT 1073 Scientific Notation
  • 原文地址:https://www.cnblogs.com/kunixiwa/p/7401821.html
Copyright © 2011-2022 走看看