网络编程笔记

网络编程

网路编程基础知识

  1. C/S和B/S架构

    C : client S : server 客户端,服务端

    B : browser S : server 浏览器,服务端

    B/S架构本质也是C/S

  2. 网络的七层协议:(应表会传网数物)

    • 物理层:电信号(0和1)

    • 数据链路层:把物理层的电信号分组,每一组叫一个数据报数据帧,每一帧有:报头head和数据data两部分。

      头固定18字节:6:发送者地址/6:接收者地址/6:数据类型

    • 网络层:

      • ip:ipv4:32位2进制表示:点分十进制表示

      • 子网掩码:通过子网掩码和ip判断两个ip是否处于同一个网段,通过ip地址和子网掩码做按位与运算

        ip地址: 172.16.10.1: 10101100.00010000.00001010.000000001
        子网掩码:255.255.255.0: 11111111.11111111.11111111.000000000
        按位与运算:172.16.10.0 10101100.00010000.00001010.000000000

      • ip和mac有转换关系:主要是因为ARP协议mac地址学习

    • 传输层:

      • tcp协议:

        • 三次握手、四次挥手
        • dos和ddos攻击:拒接服务攻击,分布式的拒接服务攻击
        • 端口号:0—65535,0—1023为系统占用端口
      • udp协议:

        直接发送,不需要响应,所以数据不可靠。(看视频花屏)

      • 端口:

        • 通过ip + 子网掩码确定一台设备
        • 通过ip + 子网掩码 + 端口号确定一个软件
    • 会话层:使应用建立和维持会话,并能使会话获得同步。

    • 表示层:作用之一是为异种机通信提供一种公共语言,以便能进行互操作。

    • 应用层:应用层向应用程序提供服务,这些服务按其向应用程序提供的特性分成组,并称为服务元素。

网络编程常用模块

模块 描述
socket 基于传输层TCP、UDP协议进行网络编程的模块
asyncore socket模块的异步版,支持基于传输层协议的异步通信
asynchat asyncore的增强版
cgi 基本的CGI(Common Gateway Interface,早期开发动态网站的技术)支持
email E-mail 和 MIME消息处理模块
ftplib 支持FTP协议的客户端模块
httplib、http.client 支持HTTP协议以及HTTP客户端模块
imaplib 支持IMAP4协议的客户端模块
mailbox 操作不同格式邮箱的模块
mailcap 支持Mailcap文件处理的模块
nntplib 支持NTTP协议的客户端模块
smtplib 支持SMTP协议(发送邮件)的客户端模块
poplib 支持POP3协议的客户端模块
telnetlib 支持TELNET协议的客户端模块
urllib 支持URL处理的模块
xmlrpc 支持XML-RPC协议的服务器和客户端模块

接下来我们会主要讨论socket模块

socket模块

程序再使用socket之前,必须先创建socket对象,可以通过以下语法创建socket实例:

s = socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
  • family 参数用于指定网络类型。总共有三个值:AF_INET(基于IPv4协议的网络)、AF_INET6(基于IPv6协议的网络)、AF_UNIX(UNIX网络)
  • type 参数用于指定网络Sock类型。默认是SOCK_STREAM(基于TCP协议的socket)、SOCK_DGRAM(基于UDP协议的socket)、SOCK_RAM(原始socket)
  • 后两个可以忽略

那么创好socket对象后就需要区分服务端和客户端了。

socket对象方法

作为服务器端使用的socket必须绑定到指定IP地址和端口,并在该IP地址和端口进行监听,接收来自客户端的连接。

socket对象提供了如下常用方法:

  • bind(address):作为服务端使用的socket调用该方法,将socket绑定到指定address元组(IP, 端口)。
  • listen([backlog]):服务端的监听方法。
  • accept() :服务端接收来自客户端的连接。
  • recv(bufsize[,flags]):接收socket中的数据(bytes)。
  • recvfrom(bufsize[,flags]):与上方法类似,返回(bytes, address)元组。
  • send(bytes[, flags]):向socket发送数据(必须已经建立连接),通常用于基于TCP协议的网络。
  • sendto(bytes, address):用于UDP协议网络中发送数据
  • sendfile(file, offset=0, count=None):将整个文件内容都发过去。
  • connect(address):客服端连接服务器。
  • connect_ex(address):与上方法大致相同,只是程序出错时不会抛出异常,而是返回一个错误标识。
  • close():关闭连接,回收资源。

使用socket通信(TCP)

  • 服务端:

    import socket
    
    host = socket.gethostname()  # 获取本机host
    server = socket.socket()
    
    server.bind((host,8080))
    server.listen(5)
    
    while 1:
        c, addr = server.accept()  # 与客户端建立连接
        c.send(b"start:")
    
        while 1:
            '''通信循环,try为了等待客户端断开后连接其他的客户端'''
            try:
                print(f"	33[1;36m{c.recv(1024).decode()}33[0m")
                msg = input(">>").encode('utf-8')
                c.send(msg)
            except Exception:
                print("客户端断开连接了!")
                break
    
  • 客户端:

    import socket
    
    host = socket.gethostname()  # 获取本机host
    cilent = socket.socket()
    cilent.connect((host, 8080))
    
    while 1:
        '''通讯循环'''
        print(f"	33[1;36m{cilent.recv(1024).decode()}33[0m")
        msg = input(">>").encode('utf-8')
        cilent.send(msg)
    

粘包问题

TCP协议中的数据都是以数据流(type=SOCK_STREAM)的形式传递的,很容易发生几次send的数据被一次recv(接收),或者被截成了好几段。为了解决粘包问题,我们借用了数据报的思想,给每一条数据加一个固定长度的头。这里引入struct模块来创建固定的头。

示例:写一个仿SHH的客户端和服务端

  • 服务端

    import socket
    import subprocess
    import struct
    
    server = socket.socket()
    host = socket.gethostname()
    port = 8081
    
    server.bind((host, port))
    server.listen(5)
    
    while 1:
        c, addr = server.accept()
        print(addr, "连接了 >>>")
        msg = b"hello "
        le = struct.pack('i',len(msg))  # 将要发送的数据的长度封装成固定长度为4的二进制数据
        c.send(le)  # 先发送一个头
        c.send(msg) # 再发送数据
        try:
            while 1:
                ssh = c.recv(1024)
                obj = subprocess.Popen(ssh.decode('gbk'), shell=True, stdout=subprocess.PIPE , stderr=subprocess.PIPE)
                # subprocess是与系统交互的模块
                msg = obj.stdout.read() or obj.stderr.read()  # 必然拿到其中一项
                le = struct.pack('i',len(msg))  # 将结果打上头
                c.send(le)  # 与上一样
                c.send(msg)
        except Exception:
            print(" 断开连接 !")
    
  • 客户端

    import socket
    import struct
    
    client = socket.socket()
    host = socket.gethostname()
    port = 8081
    
    client.connect((host,port))
    
    while 1:
        le = struct.unpack('i', client.recv(4))[0] # 解析头,获取数据长度
        print(client.recv(le).decode('gbk'))  # shell只接收gbk编码
        ssh = input(" 请输入命令 >>")
        client.send(ssh.encode('gbk'))  # 返回执行结果
    

socketserver模块

socketserver模块实现了多线程通信。使用socketserver创建服务端:

#使用socketserver写服务端
import socketserver

#自己定义一个类,必须继承StreamRequestHandler或BaseRequestHandler
class MyTcp(socketserver.StreamRequestHandler):
    #必须重写handle方法
    def handle(self):
        '''这是服务端与客户端交互的方法'''
        try:
            while True :  #通信循环
                # print(self)
                #给客户端回消息
                #conn对象就是request
                #接收数据
                print(self.client_address)
                data=self.request.recv(1024)
                print(data)
                if len(data)==0:
                    return
                #发送数据
                self.request.send(data.upper())
        except Exception:
            pass
        
if __name__ == '__main__':
    #实例化得到一个tcp连接的对象,Threading意思是说,只要来了请求,它自动的开线程来处理连接跟交互数据
    #第一个参数是绑定的地址,第二个参数传一个类
    server=socketserver.ThreadingTCPServer(('127.0.0.1',8009),MyTcp)
    #一直在监听
    #这么理解:只要来一个请求,就起一个线程交互
    server.serve_forever()
原文地址:https://www.cnblogs.com/Du704/p/11569558.html