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

    TCP/IP五层模型讲解

    物理层

    物理层功能:主要是基于电器特性发送高低电压(电信号),高电压对应数字1,低电压对应数字0

    数据链路层

    数据链路层的功能:定义了电信号的分组方式

    以太网协议

    早期的时候各个公司都有自己的分组方式,后来形成了统一的标准,即以太网协议ethernet

    ethernet规定:

    一组电信号构成一个数据包,叫做‘帧’
    
    每一数据帧分成:报头head和数据data两部分
    head包含:(固定18个字节)
    
    发送者/源地址,6个字节
    接收者/目标地址,6个字节
    数据类型,6个字节
    data包含:(最短46字节,最长1500字节)
    
    数据包的具体内容
    head长度+data长度=最短64字节,最长1518字节,超过最大限制就分片发送
    
    

    mac地址

    每块网卡出厂时都被烧制上一个世界唯一的mac地址,长度为48位2进制,通常由12位16进制数表示(前六位是厂商编号,后六位是流水线号)

    广播

    有了mac地址,同一网络内的两台主机就可以通信了(一台主机通过arp协议获取另外一台主机的mac地址)

    ethernet采用最原始的方式,广播的方式进行通信,即计算机通信基本靠吼

    网络层

    网络层功能:引入一套新的地址用来区分不同的广播域/子网,这套地址即网络地址

    ARP协议

    作用: ARP协议其主要用作将IP地址翻译为以太网的MAC地址

    传输层

    传输层功能:建立端口到端口的通信

    TCP/UDP

    TCP协议(传输控制协议)

    tcp协议是面向链接的,面向流的,提供可靠性服务的,服务端和客户端都要有成对的socket存在,必须先启动服务端。

    粘包

    粘包是TCP协议是独有的,发送端将间隔时间小而且数据量小的数据,合并到一起发送给接收端。即面向流的通信是无消息保护边界的。

    三次握手,四次挥手

    TCP原理

    3次握手

    1、客户端发送请求报文(SYN=1,并选择一个seq=x)
    
    2、服务端接收该报文,发送确认报文(SYN=1,ACK=1,并选择一个seq = y,)
    
    3、客户端收到服务器的同步确认后,对服务器发送确认的确认(将ACK=1,确认号为y+1,而报文首部的序号为x+1,)
    

    4次挥手

    假设客户端和服务端处于连接状态,客户端主动断开连接
    
    1、客户端向服务端发送FIN报文:(FIN=1,序号seq=上一个最后传输的字节序号+1=u,发送后,客户端进入FIN-WAIT-1状态。)
    
    2、服务端接收这个报文,发送一个确认报文:(令ACK=1,确认序号ack = u+1,自己的报文序号seq=v,发送后,服务器进入CLOSE-WAIT状态。)
    
    此时TCP连接进入连接半关闭状态,服务器可能还会向客户端发送一些数据。
    
    3、如果服务端已经没有要发送的数据,则发送释放信号的报文
    
    4、客户端收到报文,发出确认报文,进入close状态
    
    服务端收到客户端发来的确认报文后,也进入close状态。
    

    UDP(用户数据报协议)

    UDP协议是无连接的,面向消息的,由于UDP支持的是一对多的模式,所以UDP协议不存在粘包现象,即面向消息的通信是有消息保护边界的。

    总结

    TCP协议虽然安全性很高,但是网络开销大,而UDP协议虽然没有提供安全机制,但是网络开销小。

    socket

    Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部。

    family(socket家族)

    socket.AF_UNIX:用于本机进程间通讯,为了保证程序安全,两个独立的程序(进程)间是不能互相访问彼此的内存的,但为了实现进程间的通讯,可以通过创建一个本地的socket来完成

    socket.AF_INET:(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)

    import socket
    socket.socket(socket_family,socket_type,protocal=0)
    socket_family  # 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默认值为 0。
    
    #获取tcp/ip套接字
    tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    #获取udp/ip套接字
    udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    

    服务端套接字

    s.bind()  # 绑定(主机,端口号)到套接字
    s.listen()  # 开始TCP监听
    s.accept()  # 被动接受TCP客户的连接,(阻塞式)等待连接的到来
    

    客户端套接字

    s.connect() # 主动初始化TCP服务器连接
    s.connect_ex()  # connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
    

    公共用途的套接字函数

    s.recv()            接收TCP数据
    s.send()            发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
    s.sendall()         发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
    s.recvfrom()        接收UDP数据
    s.sendto()          发送UDP数据
    s.getpeername()     连接到当前套接字的远端的地址
    s.getsockname()     当前套接字的地址
    s.getsockopt()      返回指定套接字的参数
    s.setsockopt()      设置指定套接字的参数
    s.close()           关闭套接字
    

    面向锁的套接字方法

    s.setblocking()     设置套接字的阻塞与非阻塞模式
    s.settimeout()      设置阻塞套接字操作的超时时间
    s.gettimeout()      得到阻塞套接字操作的超时时间
    

    面向文件的套接字的函数

    s.fileno()          套接字的文件描述符
    s.makefile()        创建一个与该套接字相关的文件
    

    地址重用

    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    

    简单版FTP(实现远程执行系统命令)

    服务端

    import socket
    import subprocess
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    phone.bind(('127.0.0.1', 9901)) #0-65535:0-1024给操作系统使用
    phone.listen(5)
    
    print('starting...')
    while True:  # 链接循环
        conn, client_addr = phone.accept()
        print(client_addr)
    
        while True:  # 通信循环
            try:
                # 1、收命令
                cmd = conn.recv(1024)
                if not cmd:
                    break  # 适用于linux操作系统
    
                # 、执行命令,拿到结果
                obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE)
    
                stdout = obj.stdout.read()
                stderr = obj.stderr.read()
                print("stdout:%s, stderr:%s" % (stdout, stderr))
                # 3、把命令的结果返回给客户端
                print(len(stdout)+len(stderr))
                conn.send(stdout+stderr)  # +是一个可以优化的点
    
            except ConnectionResetError:  # 适用于windows操作系统
                break
        conn.close()
    
    phone.close()
    

    客户端

    from socket import *
    
    client=socket(AF_INET,SOCK_STREAM)
    client.connect(('127.0.0.1',8080))
    
    while True:
        msg=input('>>: ').strip()
        if not msg:continue
    
        client.send(msg.encode('utf-8'))
        data=client.recv(1024)
        print(data.decode('utf-8'))
    

    FTP(实现文件传输并解决粘包)

    服务端

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # __author__:JasonLIN
    import os
    import sys
    import json
    import struct
    import socket
    BASE_DIR = os.path.dirname(os.path.abspath(__file__))
    sys.path.append(BASE_DIR)
    family_ip = "localhost"
    port = 9991
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    s.bind((family_ip, port))
    
    s.listen(5)
    
    
    def cmd_put(conn, file_name, file_size):
        file_path = os.path.join(BASE_DIR, "server_home", file_name)
        data_recv = 0
        with open(file_path, "wb") as f:
            while file_size - data_recv > 1024:
                data = conn.recv(1024)
                f.write(data)
                data_recv += len(data)
            else:
                data = conn.recv(file_size - data_recv)
                f.write(data)
                data_recv += len(data)
                
        print("文件接收完成,一共", data_recv)
        
    
    def cmd_get():
        pass
    
    
    def run():
        while True:
            try:
                print("start.....")
                conn, addr = s.accept()
                head = conn.recv(4)
                header_len = struct.unpack("i", head)[0]
                data = json.loads(conn.recv(header_len))
                print(data, type(data))
                file_name = data["file_name"]
                func = data["func"]
                if func == "put":
                    file_size = data["file_size"]
                    cmd_put(conn, file_name, file_size)
                elif func == "get":
                    cmd_get()
    
            except Exception as e:
                print(e)
                print("客户端%s断开" % conn)
    
            
    if __name__ == '__main__':
        run()
    

    客户端

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # __author__:JasonLIN
    import os,sys
    import json
    import socket
    import struct
    BASE_DIR = os.path.dirname(os.path.abspath(__file__))
    sys.path.append(BASE_DIR)
    ip_port = ("localhost", 9991)
    c = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    c.connect(ip_port)
    
    
    def put(name):
        file_dir = os.path.join(BASE_DIR, "client_home", name)
        file_size = os.path.getsize(file_dir)
        data_header = {
            "func": "put",
            "file_name": name,
            "file_size": file_size
        }
        header_bytes = bytes(json.dumps(data_header), encoding="utf-8")
        head_len_bytes = struct.pack("i", len(header_bytes))  # 4个字节
        c.send(head_len_bytes)
        c.send(header_bytes)
        
        i = 0
        with open(file_dir, "rb")as f:
            for line in f:
                c.send(line)
                i += len(line)
        print("文件传输完成,一共:", i)
        
    
    def get():
        pass
    
    
    def run():
        while True:
            cmd = input(">>").strip()  # put test.mp4
            if cmd is None:
                continue
            if cmd == "q":
                break
            cmd_list = cmd.split(" ")
            func = cmd_list[0]
            file_name = cmd_list[1]
            if func == "put":
                put(file_name)
            elif func == "get":
                get(file_name)
            else:
                print("输入有误")
                continue
         
            
    if __name__ == '__main__':
        run()
    

    面向对象版本FTP

    服务端

    import os
    import sys
    import socket
    import json
    import struct
    BASE_DIR = os.path.dirname(os.path.abspath(__file__))
    sys.path.append(BASE_DIR)
    print(BASE_DIR)
    
    
    class MyFtpServer:
        address_family = socket.AF_INET
        socket_type = socket.SOCK_STREAM
        server_dir = os.path.join(BASE_DIR, "server_home")
        max_packet_size = 8196
        request_queue_size = 5
        coding = "utf-8"
        allow_reuse_address = False
        
        def __init__(self, address, bind_and_active=True):
            self.server_address = address
            self.socket = socket.socket(self.address_family, self.socket_type)
            
            if bind_and_active:
                try:
                    self.connect()
                except:
                    self.server_close()
                    raise
        
        def connect(self):
            """建立连接"""
            if self.allow_reuse_address:
                self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                
            self.socket.bind(self.server_address)
            self.server_address = self.socket.getsockname()
            self.socket.listen(self.request_queue_size)
         
        def get_request(self):
            return self.socket.accept()
        
        def server_close(self):
            self.socket.close()
            
        def close_request(self):
            self.conn.close()
            
        def run(self):
            while True:
                try:
                    self.conn, self.addr = self.get_request()
                    print(self.conn,  self.addr)
                    while True:
                            head_strut = self.conn.recv(4)
                            if not head_strut:
                                break
                            head_len = struct.unpack("i", head_strut)[0]
                            head_dic = json.loads(self.conn.recv(head_len))
                            print(head_dic, type(head_dic))
                            func = head_dic["func"]
                            if hasattr(self, func):
                                func = getattr(self, func)
                                func(head_dic)
                except Exception:
                    self.close_request()
                    break
                            
        def put(self, head_dic):
            file_name = head_dic["file_name"]
            file_size = head_dic["file_size"]
            file_path = os.path.join(self.server_dir, file_name)
            data_recv = 0
            with open(file_path, "wb") as f:
                while file_size - data_recv > 1024:
                    data = self.conn.recv(1024)
                    f.write(data)
                    data_recv += len(data)
                else:
                    data = self.conn.recv(file_size - data_recv)
                    f.write(data)
                    data_recv += len(data)
        
            print("文件接收完成,一共", data_recv)
            
            
    s = MyFtpServer(("127.0.0.1", 9999))
    s.run()
    

    客户端

    import os
    import sys
    import socket
    import json
    import struct
    BASE_DIR = os.path.dirname(os.path.abspath(__file__))
    sys.path.append(BASE_DIR)
    print(BASE_DIR)
    
    
    class Myclient:
        address_family = socket.AF_INET
        socket_type = socket.SOCK_STREAM
        client_dir = os.path.join(BASE_DIR, "client_home")
        max_packet_size = 8196
        request_queue_size = 5
        coding = "utf-8"
        allow_reuse_address = False
        
        def __init__(self, server_address, connect=True):
            self.server_address = server_address
            self.socket = socket.socket(self.address_family,
                                        self.socket_type)
            if connect:
                try:
                    self.client_connect()
                except:
                    self.client_close()
                    raise
        
        def client_connect(self):
            self.socket.connect(self.server_address)
            
        def client_close(self):
            self.socket.close()
        
        def run(self):
            while True:
                cmd = input(">>").strip()  # put test.mp3
                if cmd is None:
                    continue
                if cmd == "q":
                    break
                cmd_list = cmd.split(" ")
                func = cmd_list[0]
                if hasattr(self, func):
                    func = getattr(self, func)
                    func(cmd_list)
        
        def put(self, cmd_list):
            file_name = cmd_list[1]
            file_dir = os.path.join(self.client_dir, file_name)
            file_size = os.path.getsize(file_dir)
            data_header = {
                "func": "put",
                "file_name": file_name,
                "file_size": file_size
            }
            header_bytes = bytes(json.dumps(data_header), encoding="utf-8")
            head_len_bytes = struct.pack("i", len(header_bytes))  # 4个字节
            self.socket.send(head_len_bytes)
            self.socket.send(header_bytes)
        
            i = 0
            with open(file_dir, "rb")as f:
                for line in f:
                    self.socket.send(line)
                    i += len(line)
            print("文件传输完成,一共:", i)
    
    
    client = Myclient(("127.0.0.1", 9999))
    client.run()
    

    socketserver实现多并发版本

    https://github.com/Jasonlincoln/FTP

    多进程版本

    服务端

    from multiprocessing import Process
    import socket
    
    
    def task(conn):
        while True:
            try:
                data = conn.recv(1024).decode('utf-8')
                print(data)
                conn.send(data.upper().encode('utf-8'))
            except ConnectionRefusedError as e:
                print(e)
                
                
    def server(ip, port):
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server.bind((ip, port))
        server.listen(5)
        while True:
            conn, addr = server.accept()
            p = Process(target=task, args=(conn,))
            p.start()
        server.close()
        
    
    if __name__ == '__main__':
        server("127.0.0.1", 9998)
    

    客户端

    import socket
    
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(("127.0.0.1", 9998))
    while True:
        msg = input(">>").strip()
        if not msg:
            continue
        client.send(msg.encode("utf-8"))
        data = client.recv(1024)
        print(data.decode("utf-8"))
    
  • 相关阅读:
    简单方法实现无刷新提交Form表单
    [LeetCode] 654. Maximum Binary Tree
    [LeetCode]3. Longest Substring Without Repeating Characters
    《设计模式之禅》读书笔记(四)之抽象工厂模式
    《设计模式之禅》读书笔记(三)之扩展工厂方法模式
    《设计模式之禅》读书笔记(二)之工厂方法模式
    《将博客搬至CSDN》
    php解析excel文件
    git 删除分支
    git 修改注释信息
  • 原文地址:https://www.cnblogs.com/Jason-lin/p/8460473.html
Copyright © 2011-2022 走看看