• 网络编程


    一,软件开发的架构

    两个程序之间的通讯的应用大致可以分为两种:

    第一种:应用类,需要安装的桌面应用

    第二种:web类,使用浏览器访问就可以直接使用的应用

    应用的本质就是两个程序之间的通讯。

    1,C/S架构

    C(Client)/S(Server)即:客户端与服务端架构。客户端一把泛指客户端应用程序exe,程序需要先安装,才能运行对操作系统环境依赖较大。

    2,B/S架构

    B(Browser)/S(Server)即:浏览器端和服务器端架构,其实也是一种客户端。只需在浏览器上过请求HTTP请求服务端相关的资源。

    解耦分治的思想:统一入口

    二,网络基础

    1,IP地址和IP协议:规定网络地址的协议叫IP协议,它定义的地址称为IP地址,采用V4版本即ipv4,规定网络地址由32位2进制表示。IP地址是一台机器在网络上的位置。

    范围:0.0.0.0---255.255.255.255,一个IP地址通常写成四段十进制数:例172.16.10.1

    本地的回环地址:127.0.0.1

    总结一下,IP协议的作用主要有两个,一个是为每一台计算机分配IP地址,另一个是确定哪些地址在同一个子网络。

    2,mac地址

    head中包含的源和目标地址由来:ethernet规定接入internet的设备都必须具备网卡,发送端和接收端的地址便是指网卡的地址,即mac地址。
    
      mac地址:每块网卡出厂时都被烧制上一个世界唯一的mac地址,长度为48位2进制,通常由12位16进制数表示(前六位是厂商编号,后六位是流水线号)

    3,arp协议:查询IP地址与mac地址的对应关系

    地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议。
      主机发送信息时将包含目标IP地址的ARP请求广播到网络上的所有主机,并接收返回消息,以此确定目标的物理地址。
      收到返回消息后将该IP地址和物理地址存入本机ARP缓存中并保留一定时间,下次请求时直接查询ARP缓存以节约资源。

    4,路由器

    路由器(Router),是连接因特网中各局域网、广域网的设备,它会根据信道的情况自动选择和设定路由,以最佳路径,按前后顺序发送信号。
    路由器(Router)又称网关设备(Gateway)是用于连接多个逻辑上分开的网络,所谓逻辑网络是代表一个单独的网络或者一个子网。当数据从一个子网传输到另一个子网时,可通过路由器的路由功能来完成。
    路由器只接受源站或其他路由器的信息,属网络层的一种互联设备。  

    5,局域网

    局域网:是指某一区域内多台计算机互联成的计算机组。 局域网是封闭型的,可以由办公室内的两台计算机组成,也可以由一个公司内的上千台计算机组成。 

    6,子网掩码

    子网掩码:所谓子网掩码,就是表示网络特征的一个参数。它形式上等同于ip地址,是一个32位二进制数字,网络部分全为1,主机部分全为0
    
    就是将某个IP地址划分分成网络地址和主机地址两部分。
    比如,已知IP地址172.16.10.1和172.16.10.2的子网掩码都是255.255.255.0,请问它们是否在同一个子网络?两者与子网掩码分别进行AND运算,
    
    172.16.10.1:10101100.00010000.00001010.000000001
    255255.255.255.0:11111111.11111111.11111111.00000000
    AND运算得网络地址结果:10101100.00010000.00001010.000000001->172.16.10.0
    
     
    
    172.16.10.2:10101100.00010000.00001010.000000010
    255255.255.255.0:11111111.11111111.11111111.00000000
    AND运算得网络地址结果:10101100.00010000.00001010.000000001->172.16.10.0
    结果都是172.16.10.0,因此它们在同一个子网络。

    7,tcp协议和udp协议

    端口:

    端口,在计算机上每一个需要需要网络通信的程序都会开一个端口
     在同一时间只会有一个程序占用一个端口,不可能在同一机器上,同一时间 有两个程序占用同一个端口
    端口范围:0-65535
    一般情况下用8000之后的端口
    ip,确定唯一一台机器;端口:确定唯一的程序
    ip+端口:找到唯一的一台机器上的唯一一个程序

    TCP协议:

    tcp协议:当应用程序通过tcp与另一个应用程序通信时,它会发送一个通信请求。这个请求必须被送到一个确切的地址。在双方“握手之后,tcp将在两个应用程序之间建立一个全双工的通信。
    TCP是因特网中的传输层协议,使用三次握手协议建立连接。当主动方发出SYN连接请求后,等待对方回答SYN+ACK[1],并最终对对方的 SYN 执行 ACK 确认。这种建立连接的方法可以防止产生错误的连接。[1] 
    TCP三次握手的过程如下:
    客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。
    服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。
    客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。
    三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了
    建立连接:三次握手
    建立一个连接需要三次握手,而终止一个连接要经过四次握手,这是由TCP的半关闭(half-close)造成的。
    (1) 某个应用进程首先调用close,称该端执行“主动关闭”(active close)。该端的TCP于是发送一个FIN分节,表示数据发送完毕。
    (2) 接收到这个FIN的对端执行 “被动关闭”(passive close),这个FIN由TCP确认。
    注意:FIN的接收也作为一个文件结束符(end-of-file)传递给接收端应用进程,放在已排队等候该应用进程接收的任何其他数据之后,因为,FIN的接收意味着接收端应用进程在相应连接上再无额外数据可接收。
    (3) 一段时间后,接收到这个文件结束符的应用进程将调用close关闭它的套接字。这导致它的TCP也发送一个FIN。
    (4) 接收这个最终FIN的原发送端TCP(即执行主动关闭的那一端)确认这个FIN。[1] 
    既然每个方向都需要一个FIN和一个ACK,因此通常需要4个分节。
    注意:
    (1) “通常”是指,某些情况下,步骤1的FIN随数据一起发送,另外,步骤2和步骤3发送的分节都出自执行被动关闭那一端,有可能被合并成一个分节。[2] 
    (2) 在步骤2与步骤3之间,从执行被动关闭一端到执行主动关闭一端流动数据是可能的,这称为“半关闭”(half-close)。
    (3) 当一个Unix进程无论自愿地(调用exit或从main函数返回)还是非自愿地(收到一个终止本进程的信号)终止时,所有打开的描述符都被关闭,这也导致仍然打开的任何TCP连接上也发出一个FIN。
    无论是客户还是服务器,任何一端都可以执行主动关闭。通常情况是,客户执行主动关闭,但是某些协议,例如,HTTP/1.0却由服务器执行主动关闭。[2] 
    终止连接:四次挥手

    udp协议:

     当应用程序希望通过UDP与一个应用程序通信时,传输数据之前源端和终端不建立连接。
    
      当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。

    udp与tcp的对比:

    tcp,传输控制协议,提供的面向连接、可靠的字节流服务,双方建立tcp连接之后,才能传输数据。

    tcp提供超时重发,丢弃重复数据,可靠的,效率低。

    udp,用户数据报协议,是一种无连接的协议,一个简单的面向数据报的运输层协议。不可靠,只是把数据传出去,并不能保证能到达目的地。传输数据报前不用建立连接,没超时重发等机制,孤传输速度很快。

    TCP(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。
    
    UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。
    tcp协议和udp协议

    8,互联网协议与osi模型

    互联网协议按照功能不同分为osi七层/tcp/ip五层或tcp/ip四层

    应用层

    传输层          tcp与udp协议

    网络层          IP协议

    数据链路层   arp协议

    物理层

    三,socket(套接字)

    1,socket层,socket是应用层与tcp/ip协议族通信的中间软件抽象层,它是一组接口。在设计模式中,socket其实就是一个门面模式。它把复杂的tcp/ip协议隐藏在socket接口后面,对用户来说一组简单的接口就是全部,让socket去组织数据,以符合指定的协议。

    socket有两种,分别是基于文件型的和基于网络型的。

    2,socket(类似于模块)的使用

    (1),基于tcp协议的socket(tcp是基于链接的,必须先动服务器,然后再启动客户端取链接服务端)

    import socket
    sk = socket.socket()
    sk.bind(('127.0.0.1',8898))  #把地址绑定到套接字
    sk.listen()          #监听链接
    conn,addr = sk.accept() #接受客户端链接
    ret = conn.recv(1024)  #接收客户端信息
    print(ret)       #打印客户端信息
    conn.send(b'hi')        #向客户端发送信息
    conn.close()       #关闭客户端套接字
    sk.close()        #关闭服务器套接字(可选)
    server端
    import socket
    sk = socket.socket()           # 创建客户套接字
    sk.connect(('127.0.0.1',8898))    # 尝试连接服务器
    sk.send(b'hello!')
    ret = sk.recv(1024)         # 对话(发送/接收)
    print(ret)
    sk.close()            # 关闭客户套接字
    client端

    (1.1)多个客户端链接服务端(同一时间只能连一个,等一个断开才能接收到另一个客户端。)

    import socket
    #tcp协议
    sk = socket.socket()     # 买手机 创建一个socket对象
    sk.bind(('127.0.0.1',8080))  # 给server端绑定一个ip和端口
    sk.listen()              # py3.4
    while True:
        conn,addr = sk.accept()  # 获取到一个客户端的连接, 已经完成了三次握手建立了一个连接
                                 # 阻塞
        while True:
            msg = conn.recv(1024).decode('utf-8')    # 阻塞,直到收到一个客户端发来的消息
            print(msg)
            if msg == 'bye':break
            info = input('>>>')
            if info == 'bye':
                conn.send(b'bye')
                break
            conn.send(info.encode('utf-8'))     # 发消息
        conn.close()             # 关闭连接
    sk.close()               # 关闭socket对象,如果不关闭,还能继续接收
    # server client1 建立了长连接
    # client2
    server端
    import socket
    sk = socket.socket()
    sk.connect(('127.0.0.1',8080))
    while True:
        msg = input('>>>')
        if msg == 'bye':
            sk.send(b'bye')
            break
        sk.send(msg.encode('utf-8'))
        ret = sk.recv(1024).decode('utf-8')
        if ret == 'bye':break
        print(ret)
    sk.close()
    client1端
    import socket
    sk = socket.socket()
    sk.connect(('127.0.0.1',8080))
    while True:
        msg = input('client2:>>>')
        if msg == 'bye':
            sk.send(b'bye')
            break
        sk.send(('client2 :'+msg).encode('utf-8'))
        ret = sk.recv(1024).decode('utf-8')
        if ret == 'bye':break
        print(ret)
    sk.close()
    client2

    (2)基于udp协议的socket(udp是无链接的,先启动哪一端都不会报错)

    import socket
    sk=socket.socket(type=socket.SOCK_DGRAM)
    sk.bind(('127.0.0.1',8090))
    
    msg,addr=sk.recvfrom(1024)
    print(msg.decode('utf-8'))
    
    sk.sendto(b'bye',addr)
    sk.close()
    server端
    import socket
    sk=socket.socket(type=socket.SOCK_DGRAM)
    ip_port=(('127.0.0.1',8090))
    
    sk.sendto(b'hi',ip_port)
    ret,addr=sk.recvfrom(1024)
    
    print(ret.decode('utf-8'))
    sk.close()
    client端

    简单聊天程序:

    import socket
    sk=socket.socket(type=socket.SOCK_DGRAM)
    sk.bind(('127.0.0.1',8090))
    while  True:                                               #仅服务端有
        msg,addr=sk.recvfrom(1024)
        print(msg.decode('utf-8'))
        if msg.decode('utf-8') == 'bye':
            break
        info = input('>>>>')
        if info=='bye':
            sk.sendto(b'bye', addr)
            break
        sk.sendto(info.encode('utf-8'),addr)
    sk.close()
    server端
    import socket
    sk=socket.socket(type=socket.SOCK_DGRAM)
    ip_port=('127.0.0.1',8090)
    while True:
        info=input('<<<')
        if info=='bye':
            sk.sendto(b'bye',ip_port)
            break
        sk.sendto(info.encode('utf-8'),ip_port)
    
        ret,addr=sk.recvfrom(1024)
        if ret.decode('utf-8') == 'bye': break
        print(ret.decode('utf-8'))
    
    sk.close()
    client端
    import socket
    sk=socket.socket(type=socket.SOCK_DGRAM)
    ip_port=('127.0.0.1',8090)
    while True:
        info=input('<<<')
        if info=='bye':
            sk.sendto(b'bye',ip_port)
            break
        sk.sendto(('第三个:'+info).encode('utf-8'),ip_port)
        ret,addr=sk.recvfrom(1024)
        if ret.decode('utf-8') == 'bye': break
        print(ret.decode('utf-8'))
    
    sk.close()
    client端

    时间服务器:

    import time
    import socket
    sk=socket.socket(type=socket.SOCK_DGRAM)
    sk.bind(('127.0.0.1',8090))
    msg,addr=sk.recvfrom(1024)
    time_1=msg.decode('utf-8')
    time.strftime(time_1)
    
    sk.sendto((time.strftime(time_1)).encode('utf-8'),addr)
    sk.close()
    server端
    import logging
    import socket
    logging.basicConfig(level=logging.DEBUG,
                        format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                        datefmt='%a, %d %b %Y %H:%M:%S',
                        filename='test.log',
                        filemode='a')
    
    sk=socket.socket(type=socket.SOCK_DGRAM)
    ip_port=('127.0.0.1',8090)
    sk.sendto(('%Y-%m-%d %X').encode('utf-8'),ip_port)
    ret,addr=sk.recvfrom(1024)
    print(ret.decode('utf-8'))
    logging.info(ret.decode('utf-8'))
    
    sk.close()
    client端
  • 相关阅读:
    The 2019 China Collegiate Programming Contest Harbin Site A
    牛客练习赛15
    Wannafly挑战赛13-C
    Wannafly挑战赛13-D
    Subsequence Counting
    Minimize the error
    Educational Codeforces Round 42 (Rated for Div. 2)
    K-th Number
    Wannafly挑战赛13-E
    Minimum spanning tree for each edge
  • 原文地址:https://www.cnblogs.com/glf1160/p/8361013.html
Copyright © 2020-2023  润新知