网络编程

Posted wateligx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络编程相关的知识,希望对你有一定的参考价值。

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()

 

 

以上是关于网络编程的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段14——Vue的axios网络请求封装

VSCode自定义代码片段14——Vue的axios网络请求封装

VSCode自定义代码片段14——Vue的axios网络请求封装

使用 Pygments 检测代码片段的编程语言

面向面试编程代码片段之GC

如何在 Django Summernote 中显示编程片段的代码块?