zoukankan      html  css  js  c++  java
  • 网络编程 架构 TCP UDP 网络编程(32--35)

    1、计算机网络分类
    虽然网络类型的划分标准各种各样,但是从地理范围划分是一种大家都认可的通用网络划分标准。按这种标准可以把各种网络类型划分为局域网、城域网、广域网三种。
    局域网:一般来说只能是一个较小区域内
    城域网:是不同地区的网络互联,不过在此要说明的一点就是这里的网络划分并没有严格意义上地理范围的区分,只能是一个定性的概念。
    广域网:所覆盖的范围比城域网(MAN)更广,它一般是在不同城市之间的LAN或者MAN网络互联,地理范围可从几百公里到几千公里,Internet就是一种广域网
    架构  ---程序员开发的一种模式
    CS :Client与Server ,中文意思:客户端与服务器端架构,这种架构也是从用户层面(也可以是物理层面)来划分的。
    B/S:Browser与Server,中文意思:浏览器端服务器端架构,这种架构是从用户层面来划分的。
    B/S架构的优势:统一了应用的接口
    网络通信编程:
    同一台电脑上两个py文件通信   ---   文件
         
         两台电脑  如何通信?          ---   一根网线
         
         多台电脑  如何通信?          ---   交换机
         
         更多台电脑   如何通信?       ---   交换机+路由器

    早期 : 联机

    以太网 : 局域网与交换机

    广播
    主机之间“一对所有”的通讯模式,网络对其中每一台主机发出的信号都进行无条件复制并转发,所有主机都可以接收到所有信息(不管你是否需要),由于其不用路径选择,所以其网络成本可以很低廉。有线电视网就是典型的广播型网络,我们的电视机实际上是接受到所有频道的信号,但只将一个频道的信号还原成画面。在数据网络中也允许广播的存在,但其被限制在二层交换机的局域网范围内,禁止广播数据穿过路由器,防止广播数据影响大面积的主机。
    View Code

    ip地址与ip协议

    规定网络地址的协议叫ip协议,它定义的地址称之为ip地址,广泛采用的v4版本即ipv4,它规定网络地址由32位2进制表示
    范围0.0.0.0-255.255.255.255
    一个ip地址通常写成四段十进制数,例:172.16.10.1
    View Code

    mac地址

    head中包含的源和目标地址由来:ethernet规定接入internet的设备都必须具备网卡,发送端和接收端的地址便是指网卡的地址,即mac地址。
    
      mac地址:每块网卡出厂时都被烧制上一个世界唯一的mac地址,长度为48位2进制,通常由12位16进制数表示(前六位是厂商编号,后六位是流水线号)
    View Code
    网卡: mac 地址    物理地址
    一般是由网卡生产厂商给的,不能随便给,要遵循一定的规则,为什么?
    因为mac 地址要求全球统一
    服务器1找寻服务器2的mac地址的过程:
    1,发送请求帧给交换机
    2,交换机以广播方式找寻服务器2
    3,服务器2接收到请求返回给交换机
    4,交换机以单播方式返回给服务器1
    整个过程就是服务器1 通过服务器 2 的ip地址,借助交换机的功能,找到了服务器2的mac地址
    这就是arp协议
    arp协议: 通过目标ip地址,获取目标mac地址
    在cmd 查看命令  ipconfig -all
    mac地址是12位十六进制组成
     ip地址: 是一个四位点分十进制组成(ipv4)
     端口:   操作系统为每一个应用程序分配一个端口号
    ip + 端口号 唯一确定某一个电脑上的某一个程序
    给你一个ip地址,如何确定它是不是在你当前局域网内?
     ip地址    & (与)    子网掩码
    ex:
     ip地址    192.168.1.12
            1100 0000 . 1010 1000 . 0000 0001 . 0000 1100  # (192.168.1.12的二进制结果)
            
        1111 1111 . 1111 1111 . 1111 1111 . 0000 0000     # (255.255.255.0的二进制结果)
                    
    结果    :1100 0000 . 1010 1000 . 0000 0001 . 0000 0000   #(上面两数相成结果)
         ---> 192.168.1.0 网段
        子网掩码  255.255.255.0
    用网段来确定一个ip地址是否在你当前的局域网内.
     网段 = ip地址    &     子网掩码
    路由器:有一个路由表,记录了归它管理的所有的网段
     协议 : 由多人制定的一种规则

    子网掩码:

     所谓”子网掩码”,就是表示子网络特征的一个参数。它在形式上等同于IP地址,也是一个32位二进制数字,它的网络部分全部为1,主机部分全部为0。比如,IP地址172.16.10.1,如果已知网络部分是前24位,主机部分是后8位,那么子网络掩码就是11111111.11111111.11111111.00000000,写成十进制就是255.255.255.0。
      知道”子网掩码”,我们就能判断,任意两个IP地址是否处在同一个子网络。方法是将两个IP地址与子网掩码分别进行AND运算(两个数位都为1,运算结果为1,否则为0),然后比较结果是否相同,如果是的话,就表明它们在同一个子网络中,否则就不是。 
    View Code
    通过socket模块去操作tcp和udp协议
    TCP协议 : 安全可靠通信方式,面向连接
    UDP协议 : 不安全,不可靠的通信方式,快,(如视频通话)
     ip地址:127.0.0.1 : 回环地址:无论什么时候,都可以代表本机的ip地址(只限在本机使用)

    3.socket概念

    socket层

    理解socket

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

    3.套接字(socket)的发展史

    套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。 

    基于文件类型的套接字家族

    套接字家族的名字:AF_UNIX

    unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信

    基于网络类型的套接字家族

    套接字家族的名字:AF_INET

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

    四.套接字(socket)初使用

    基于TCP协议的socket

    tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
    
    

    server端

    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()        #关闭服务器套接字(可选)

    client端

    import socket
    sk = socket.socket()           # 创建客户套接字
    sk.connect(('127.0.0.1',8898))    # 尝试连接服务器
    sk.send(b'hello!')
    ret = sk.recv(1024)         # 对话(发送/接收)
    print(ret)
    sk.close()            # 关闭客户套接字

    问题:有的同学在重启服务端时可能会遇到

    解决方法:

    #加入一条socket配置,重用ip和端口
    import socket
    from socket import SOL_SOCKET,SO_REUSEADDR
    sk = socket.socket()
    sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
    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()        #关闭服务器套接字(可选)
    View Code

    基于UDP协议的socket

    udp是无链接的,启动服务之后可以直接接受消息不需要提前建立链接

    简单使用

    server端

    import socket
    udp_sk = socket.socket(type=socket.SOCK_DGRAM)   #创建一个服务器的套接字
    udp_sk.bind(('127.0.0.1',9000))        #绑定服务器套接字
    msg,addr = udp_sk.recvfrom(1024)
    print(msg)
    udp_sk.sendto(b'hi',addr)                 # 对话(接收与发送)
    udp_sk.close()                         # 关闭服务器套接字

    client端

    import socket
    ip_port=('127.0.0.1',9000)
    udp_sk=socket.socket(type=socket.SOCK_DGRAM)
    udp_sk.sendto(b'hello',ip_port)
    back_msg,addr=udp_sk.recvfrom(1024)
    print(back_msg.decode('utf-8'),addr)

    qq聊天

    #_*_coding:utf-8_*_
    import socket
    ip_port=('127.0.0.1',8081)
    udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    udp_server_sock.bind(ip_port)
    
    while True:
        qq_msg,addr=udp_server_sock.recvfrom(1024)
        print('来自[%s:%s]的一条消息:33[1;44m%s33[0m' %(addr[0],addr[1],qq_msg.decode('utf-8')))
        back_msg=input('回复消息: ').strip()
    
        udp_server_sock.sendto(back_msg.encode('utf-8'),addr)
    服务器
    #_*_coding:utf-8_*_
    import socket
    BUFSIZE=1024
    udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    
    qq_name_dic={
        '金老板':('127.0.0.1',8081),
        '哪吒':('127.0.0.1',8081),
        'egg':('127.0.0.1',8081),
        'yuan':('127.0.0.1',8081),
    }
    
    
    while True:
        qq_name=input('请选择聊天对象: ').strip()
        while True:
            msg=input('请输入消息,回车发送,输入q结束和他的聊天: ').strip()
            if msg == 'q':break
            if not msg or not qq_name or qq_name not in qq_name_dic:continue
            udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])
    
            back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)
            print('来自[%s:%s]的一条消息:33[1;44m%s33[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))
    
    udp_client_socket.close()
    客户端

    时间服务器

    # _*_coding:utf-8_*_
    from socket import *
    from time import strftime
    
    ip_port = ('127.0.0.1', 9000)
    bufsize = 1024
    
    tcp_server = socket(AF_INET, SOCK_DGRAM)
    tcp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    tcp_server.bind(ip_port)
    
    while True:
        msg, addr = tcp_server.recvfrom(bufsize)
        print('===>', msg)
    
        if not msg:
            time_fmt = '%Y-%m-%d %X'
        else:
            time_fmt = msg.decode('utf-8')
        back_msg = strftime(time_fmt)
    
        tcp_server.sendto(back_msg.encode('utf-8'), addr)
    
    tcp_server.close()
    服务器
    #_*_coding:utf-8_*_
    from socket import *
    ip_port=('127.0.0.1',9000)
    bufsize=1024
    
    tcp_client=socket(AF_INET,SOCK_DGRAM)
    
    
    
    while True:
        msg=input('请输入时间格式(例%Y %m %d)>>: ').strip()
        tcp_client.sendto(msg.encode('utf-8'),ip_port)
    
        data=tcp_client.recv(bufsize)
    客户端

    TCP服务器代码:
    import socket sk = socket.socket(type = socket.SOCK_STREAM) # 采用tcp协议 买了个新手机 sk.bind(('192.168.19.200',9090)) # 端口号范围:0-65535 0 - 1023(不能用) 8000 - 10000 (常用) 买一个手机卡 sk.listen() # 告诉好友我的新手机号 print(123) conn,addr = sk.accept() # 等待好友来给我打电话 print(456) msg = conn.recv(1024) # 聊天 print(msg.decode('utf-8'),addr,conn) conn.send(b'hahaha') conn.close() # 挂掉电话 sk.close() # 关机 客户端: import socket sk = socket.socket() sk.connect(('127.0.0.1',9200)) sk.send(b'hello') sk.close()
    TCP协议:面试题
      三次握手 : 注意必须是client先发起请求
    1 ,client发送给server我想连接你,可以么
    2 ,server回复client,可以,我也想连接你,可以吗?
    3 ,client回复server,可以
     四次挥手 : 谁先发起请求都可以
    1, client发送请求:我准备断开连接了,我没有数据需要发送了, 如果你有数据可以继续发给我
    2 ,server回复:确认接收到你的请求,我开始着手准备断开事宜
    3, server回复:我准备好了,可以随时断开连接
    4, client回复:断开连接吧

     这个全双工的通信将占用两个计算机之间的通信线路,直到它被一方或双方关闭为止。

    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] 
    四次握手

    互联网协议与osi模型

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

    互联网协议与osi模型
    互联网协议按照功能不同分为osi七层或tcp/ip五层或tcp/ip四层
     OSI五层模型:(会背)
    应用层       py文件
    传输层       tcp/udp协议
    网络层       ip协议
    数据链路层   arp协议,网卡
    物理层       网线,集线器,光纤

    每层运行常见物理设备

    每层运行常见的协议

    4.tcp协议和udp协议

    TCP(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。

    UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。

    我知道说这些你们也不懂,直接上图。

    补充:

    TCP/IP协议
    TCP/IP协议不是TCP和IP这两个协议的合称,而是指因特网整个TCP/IP协议族
    在TCP/IP协议中,最重要的协议是TCP、UDP、IP协议
    1、TCP协议(Transmission Control Protocol)
    TCP是基于面向连接的协议,也就是说,在正式收发数据前,必须和对方建立可靠的连接。 
    TCP协议建立连接需要三次会话(握手)
    A->B
    B->A
    A->B
    特点:面向连接,安全,可靠 , 效率相对较低 , 数据大小无限制
    2、UDP协议(User Datagram Protocol)
    UDP是面向无连接的通讯协议,UDP数据包括目的端口号和源端口号信息,由于通讯不需要连接,所以可以实现广播发送。
    UDP通讯时不需要接收方确认,属于不可靠的传输,可能会出现丢包现象,实际应用中要求程序员编程验证
    特点:无连接,可靠性不高,效率高,每次最大传输64KB
    3、IP协议
    IP的责任就是把数据从源传送到目的地。它不负责保证传送可靠性,流控制,包顺序和其它对于主机到主机协议来说很普通的服务。
    IP协议中还有一个非常重要的内容,那就是给因特网上的每台计算机和其它设备都规定了一个唯一的地址,叫做“IP 地址”。
    4、IP地址(Internet  Protocol)
    唯一标识网络上的每一台计算机
    IP地址的组成: 32位,由4个8位二进制数组成
    5、配置IP地址,检测网络是否畅通
    查看本机的IP地址:ipconfig
    测试网络是否通畅:ping目标主机ip地址
    6、端口号
    端口号:具有网络功能的应用软件的标识号
    端口是一个软件结构,被客户程序或服务程序用来发送和接收数据,一台服务器有256*256个端口。
    0-1023是公认端口号,即已经公认定义或为将要公认定义的软件保留的
    1024-65535是并没有公共定义的端口号,用户可以自己定义这些端口的作用。
    端口与协议有关:TCP和UDP的端口互不相干。
    7、网络服务器
    通常指在网络环境下,具有较高计算能力,能够提供用户特殊服务功能的计算机
    Socket(套接字)介绍
    通信链路的端点就被称为“套接字”(英文名Socket)
    是提供给应用程序的接口
    1、Socket套接字
    两个应用程序可以通过一个双向的网络通信连接实现数据交换,这个双向链路的一端称为一个Socket。
    Socket可以看成在两个程序进行通讯连接中的一个端点,一个程序将一段信息写入Socket中,该Socket将这段信息发送给另外一个Socket中,使这段信息能传送到其他程序中。
    Socket通常用来实现client-server(客户端-服务端)连接,建立连接时所需的寻址信息为远程计算机的IP地址和端口号(Port Number)。
    2、网络编程的四个基本步骤
    (1)创建socket;
    (2)打开连接到socket的输入/输出流(通过Socket的getInputStream()和getOutputStream());
    (3)按照一定的协议对socket进行读/写操作;
    (4)关闭socket;
    3、基于TCP协议的Socket编程
    (1)基于TCP协议的Socket网络通信
    用来实现双向安全连接网络通信
    TCP Socket通常用来实现client-server连接,建立连接时所需的寻址信息为远程计算机的IP地址和端口号(Port Number)
    (2)TCP Socket通信模型
    进行网络通信时,Socket需要借助数据流来完成数据的传递工作。
    一、基础概念
    
    1、网络架构
    
    Client/Server结构(C/S结构)客户机和服务器结构。本文的主角。B/S结构(Browser/Server,浏览器/服务器模式),WEB浏览器是客户端最主要的应用软件。
    
    2、IP
    
    IP地址是网路通信寻址的主要手段
    
    3、端口(port )
    
    每台计算机有很多个端口。通常是一个进程(运行着的程序)对应一个端口,访问该主机的某个端口就是访问对应的进程。有些端口是默认的对应一些进程,像其中80端口分配给WWW服务,21端口分配给FTP服务。通常我们选用1024以上的端口。
    
    4、socket(套接字)
    
    套接字是一个很抽象的概念。可以理解为连接两个端口之间一条虚拟的线,而端口就是虚拟的接口,或者可以理解为电源线跟插头的关系。要进行网络通信,少了它不行。
    
    二、c/s架构
    
    s是服务器(运行了服务端程序的计算机),c是客户机。c向s发送消息(请求),s接受到消息并处理(响应)。建立连接后s也可以主动向c发送消息,通常是多个c对应一个s。这里我用的只有一个客户端。
    
    三、tcp通信
    
    1、连接通信
    
    在进行通信前,需要做一个虚连接。即客户端c想要与服务端s进行通信必须先要与之进行连接。
    
    2、客户端
    
    客户端通信很简单,只要取得与服务端的联系就可以进行信息的收发。
    
    3、服务端
    
    服务端是一个进程,总是等待着客户端发来请求,并处理相应的请求。
    
    四、通信过程
    
    不涉及具体调用函数的说明。
    
    1、客户端设计
    
    第一步、确定要进行连接的服务端的信息(IP、port)
    
    第二步、获取一个socket,调用相应的函数得到
    
    第三步、用得到的socket与服务端的信息去调用connect与服务端进行连接
    
    第四部、收发消息(send/recv)
    
    第五步、关闭打开的套接字
    
    2、服务端的设计
    
    第一步、确定本机IP和要与程序绑定的端口
    
    第二步、获取一个监听用的socket。
    
    第三步、将得到的socket与确定后的端口绑定,调用bind.
    
    第四步、监听。坐等客户端的到来(没来是一个处于阻塞的函数,调用 listen)
    
    第五步、接受客户端的请求,并建立一个新的套接字来与客户端通信(accept)
    
    第六步、用建立的套接字与客户端通信(send/recv)。
    
    第七步、关闭已经打开的套接字,跟文件处理是一样的。
    
    
    
     
  • 相关阅读:
    Codeforces Round #548
    省选前的th题
    省选前多项式的挣扎
    2019.3.18考试&2019.3.19考试&2019.3.21考试
    省选前的反演抢救计划
    2019.3.16 noiac的原题模拟赛
    AtCoder Regular Contest 069 F
    Atcoder Grand 012 C
    Atcoder Grand 011 C
    Atcoder Grand 006 C-Rabbit Exercise
  • 原文地址:https://www.cnblogs.com/ls13691357174/p/9325409.html
Copyright © 2011-2022 走看看