跳转至

Network Programming 网络编程

一、网络通讯

网络通讯概述

  • 多方链接,数据传递

IP地址

用途

  • 唯一标记网络中一个终端

组成

  • 网络地址
  • 主机地址

分类

  • IPV4

    • A类

      • 1Byte网络地址 + 3Byte主机地址, 最高位为0
      • 1.0.0.1-126.255.255.254
    • B类

      • 2Byte网络地址 + 2Byte主机地址, 最高位为10
      • 128.1.0.1-191.255.255.254
    • C类

      • 3Byte网络地址 + 1Byte主机地址, 最高位为110
      • 192.0.1.1-223.255.255.254
    • D类

      • 最高位为1110
      • 保留地址,不制定特定网络,多用于多点广播
      • 224.0.0.1-239.255.255.254
    • E类

      • 以“1111”开始,为将来使用保留
      • 仅用于实验和开发
    • 私有IP

      • IP地址仅用于局域网使用,不可在公网中使用
      • 10.0.0.0~10.255.255.255
      • 172.16.0.0~172.31.255.255
      • 192.168.0.0~192.168.255.255
  • IPV6

  • 其他

    • IP地址127.0.0.1 ~ 127.255.255.255 用于回路测试
    • 127.0.0.1 代表本地IP地址,那么,http://127.0.0.1就可测试本机配置的Web服务器

端口Port

  • 应用门户
  • 端口号

    • 0 - 65535
  • 分配

    • 知名端口

      • 0到1023
      • 80端口分配给HTTP服务
      • 21端口分配给FTP服务
    • 动态端口

      • 1024到65535
      • 主机分配可用端口,而后随程序结束而释放

查看

netstat -an

socket

  • 不同电脑的进程间通讯

    • PID?
    • IP地址 + 端口
  • 基本概念

    • 进程间通讯的一种方式
  • 实践

    • 创建套接字

      import socket
      
      sock = socket.socket(AddressFamily, Type)
      
      sock.close()
      
      • Address Family:可以选择
        • AF_INET(用于 Internet 进程间通信,实际工作**常用**)
        • AF_UNIX(用于同一台机器进程间通信)
      • Type:套接字类型
        • SOCK_STREAM(流式套接字,主要用于 TCP 协议)
        • SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)
  • 使用流程

    • 创建套接字
    • 使用套接字收/发数据
    • 关闭套接字

二、UDP网络程序

  • 具体步骤
  • 端口绑定问题解决

三、TCP网络程序

  • 基本概念

    • 传输控制协议Transmission Control Protocol
    • 面向连接的、可靠的、基于字节流的传输层通信协议
  • 特点

    • 面向连接
    • 可靠传输

      • 发送应答机制

        • ACK
      • 超时重传

        • RTT
      • 错误校验

      • 流量控制和阻塞管理
  • 通讯模型

    • 打电话

TCP客户端

TCP服务器

  • 基本流程

    • socket创建一个套接字
    • bind绑定ip和port
    • listen使套接字变为可以被动链接
    • accept等待客户端的链接
    • recv/send接收发送数据

TCP 注意点

  • 服务器端口绑定

三次握手,四次挥手

  1. 三次握手,四次挥手都是在tcp协议中发生的,这种协议是全双工通信协议,即允许数据同时在两个方向上同时传输,同时进行收发操作。
  2. 而三次握手,四次挥手两者都是客户端首先发送相关命令信息。这与端口的绑定有关。

三次握手:是为了客户端与服务端都准备好的连接工作

  1. 客户端在连接服务器时进行堵塞,发送syn number标志。
  2. 而后服务器端接受到信息进行应答, 准备好资源。发送 ack number + 1;同时也向客户端发送syn num的信息
  3. 而后客户端接收到信息,将Num + 1后回送给服务器端

四次挥手:是为了客户端与服务端都进行资源的释放操作

  1. 连接断开的时候,客户端先发送连接套接字关闭的命令,
  2. 而后服务器端在接收到关闭命令之后,发送关闭接受命令回送信息。
  3. 再然后,会调用子套接字的关闭命令,发送给客户端,进行关闭发送命令。
  4. 客户端接收到服务器的关闭命令后,进行延迟2msl时间延迟操作,同时回送已接收到关闭命令的信息。在这个过程中,如果服务器端没有接收到回送信息,则会重复再发送子套接字关闭发送的命令。

可以设置服务器的套接字可以重复利用之前的资源。

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

TCP 长断连接

  • 现在的网站连接都是利用长连接,以减少服务器的资源。短连接使用需要耗费大量的资源

四、网络抓包

wireshark

五、TCP/IP协议

TCP/IP简介

  • 协议

  • 网络协议

  • TCP/IP协议簇

  • 常用网络协议

六、网络通讯过程

  • 两台电脑通讯

    • IP地址处于同一网段
  • 集线器组成网络

    • 缺点

      • 广播发送,容易网络拥堵
  • 交换机组成网络

    • 即可广播,也可单播
  • 路由器连接多网络

通讯过程

  1. 首先去系统缓存文件中寻找是否有域名对应的ip地址,如果没有则需要去访问DNS服务器获取该域名对应的IP地址。

    1. 电脑去系统缓存文件中查看是否有默认网关(路由器)的Mac地址,arp -a
    2. 如果有,直接使用。如果没有则使用arp协议广播获取路由器的Mac地址
    3. 依据DNS服务器的IP地址,发送请求解析对应的域名
    4. 而后,通过默认网关以及互联网的层层转发到达DNS域名服务器,获得请求并应答回递。
  2. 知道了域名服务器对应的IP地址,之后则向域名对应的IP地址发送三次握手连接请求。连接成功。

  3. 而后服务器进行HTTP协议数据应答。
  4. 之后便可以通过客户端进行相应的HTTP的请求数据发送以及等待服务器的相关应答操作。
  5. 访问结束后,客户端与服务器进行TCP的四次挥手。双方进行相关操作资源的释放。

当然,期间还有路由器之间的发射协议

网络地址转换器NET

七、Http 协议

八、Web并发服务器

Epoll原理

当进程单线程实现高并发: - kernel: 操作系统拥有独有的核心空间

单进程单线程瓶颈: - 将服务器内部的套接字进行复制FD(文件描述符)后,让操作系统检查FD内部是否有新消息到达

共享的内存空间中进行检查: - 减少复制的内存时间 - 以事件通知的方式进行检查,而非轮询检查。

import epoll

# 1. 创建epoll对象
epoll = select.epoll()

# 将监听套接字对应的ID注册到epoll中
epoll.register(server_socket.fileno(), select.EPOLLIN)


while True:

    # 默认堵塞,直到OS检测到数据来,通过事件通知的方式进行告诉这个程序,此时才会解堵塞。
    # return: [(fd, event), (fd2, event2), (fd3, event3)]
    fd_event_list = epoll.poll()

    # [(fd, event), (套接字对应的文件描述符,这个文件描述符到底是什么事件,例如可以调用recv接收等)]
    for fd, event in fd_event_list:

        if fd == server_socket.fileno():
            # 判断新客户端连接到来

            client_socket, client_addr = server_socket.accept()

            epoll.register(client_socket.fileno(), select.EPOLLIN)

            # 添加字典,从文件描述符获取到套接字
            fd_event_dict[client_socket.fileno()] = client_socket

        elif event = select.EPOLLIN:
            # 判断已经连接的客户端有数据发送过来
            recv_data =  fd_event_dict[fd].recv(128).decode("utf-8")

            if recv_data:
                # 有数据消息
                service_client(fd_event_dict[fd], recv_data)

            else:
                # 没有消息,客户端主动关闭
                fd_event_dict[fd].close()
                epoll.unresigster(fd)
                del fd_event_dict[fd]