网络编程笔记

网络编程

网路编程基础知识

  1. C/S和B/S架构python

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

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

    B/S架构本质也是C/S浏览器

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

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

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

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

    • 网络层:ssh

      • 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"\t\033[1;36m{c.recv(1024).decode()}\033[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"\t\033[1;36m{cilent.recv(1024).decode()}\033[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()
相关文章
相关标签/搜索