zoukankan      html  css  js  c++  java
  • TCP协议和UDP协议

     

    1.socket模块中的TCP协议

    • socket模块在osi模型中的虚拟位置

    Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
    
    所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
    
    也有人将socket说成ip+port,ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序,ip地址是配置到网卡上的,而port是应用程序开启的,ip与port的绑定就标识了互联网中独一无二的一个应用程序
    
    而程序的pid是同一台机器上不同进程或者线程的标识
    socket概念

    a:格式:sk = socket.socket()

    b:参数:sk = socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)

    c:基于TCP协议的套接字的流程:

    #用TCP协议循环对话:

    #client客户端
    import socket
    sk = socket.socket()
    sk.connect(('127.0.0.1',65534))
    while 1:
        mes = input(">>>:")
        sk.send(mes.encode('utf-8'))
        if mes.upper() == 'Q':
            break
        msg = sk.recv(1024).decode('utf-8')
        if msg.upper() == 'Q':
            break
        print(msg)
    sk.close()
    #serve服务器端
    import socket
    sk = socket.socket()
    sk.bind(('127.0.0.1',65534))
    sk.listen()
    while 1:
        conn,addr = sk.accept()    conn指的就是连接对象
        while 1:
            message = conn.recv(1024).decode('utf-8')
            print(message)
            if message.upper() == 'Q':
                break
            mes = input(">>>:")
            conn.send(mes.encode('utf-8'))
            if mes.upper() == 'Q':
                break
        conn.close()
    sk.close()


    #
    tcp_sk = socket.socket()
    ret = tcp_sk.connect_ex(('127.0.1.1',8091))
    tcp_sk.send('nihao'.encode('utf-8'))
    tcp_sk.close()
    ***connect_ex跟connect的区别,connect_ex错误的时候有返回值,返回数字10061,然后往下执行;报的是下一行的错
      但是connect直接在本行报错
    
    

    #这两段代码中牵扯到一个tcp的三次握手和四次挥手:

    三次握手:

    • 一定是client先发起请求
    • a  客户端发起请求连接服务器
    • b  服务器返回 : 接收到请求,并要求连接客户端
    • c  客户端回复 : 可以连接

    四次挥手:

    • 谁先发起断开连接的请求都可以
    • a 客户端发起断开连接的请求:我想和你断开连接,我没有数据要继续发送了,但是如果你有数据需要发送,我可以继续接收
    • b 服务器回复 : 我接收到你的请求了
    • c 服务器发送 : 我已经准备好断开连接了
    • d 客户端回复 : 收到你的信息,断开连接

    几个专有名词:

    • ACK:确认收到
    • SYN:请求连接的这么一个标识
    • FIN:请求断开的这么一个标识
    • 断开连接时,因为双方有数据产生了,所以服务端接到请求时,三次握手的中间一步必须分成两步来写了!

    关于操作系统和端口如果重复使用时,会被报错,可能操作系统没有及时清理端口信息,你重开服务器时,会遇到的错误

    解决方案:

    #加入一条socket配置,重用ip和端口
    
    phone=socket(AF_INET,SOCK_STREAM)
    phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
    phone.bind(('127.0.0.1',8080))

    2.UDP协议及编码

    a:sk = socket.socket(type=socket.SOCK_DGRAM)

    b:回环地址:每个计算机都有,只能用于本机,不能作为其他计算机解析这个ip地址;

    c:udp跟多个对象聊天代码:

    #serve服务器
    import socket
    udp_sk = socket.socket(type=socket.SOCK_DGRAM)
    udp_sk.bind(('127.0.0.1',65534))
    while 1:
        msg,addr = udp_sk.recvfrom(1024)
        if msg.decode('utf-8').upper() == "Q":
            break
        print(msg.decode('utf-8'))
        msg_send = input('>>>:')
        udp_sk.sendto(msg_send.encode('utf-8'),addr)
        if msg_send.upper() == "Q":
            break
    udp_sk.close()
    #client客户端
    import socket
    ip_sk = ('127.0.0.1',65534)
    udp_sk = socket.socket(type=socket.SOCK_DGRAM)
    while 1:
        msg_send = input('>>>:')
        udp_sk.sendto(msg_send.encode('utf-8'),ip_sk)
        if msg_send.upper() == "Q":
            break
        back_msg,addr = udp_sk.recvfrom(1024)
        print(back_msg.decode('utf-8'))
        if back_msg.decode('utf-8').upper() == 'Q':
            break
    udp_sk.close()

     3.粘包问题

    1.问题引出:看下面的两端程序

    服务器import socket
    sk = socket.socket()            
    
    sk.bind(('127.0.0.1',8888))
    sk.listen()
    
    conn,addr = sk.accept()
    
    conn.send(b'hello')
    conn.send(b'world')
    
    conn.close()
    sk.close()
    
    
    #客户端
    import socket
    sk = socket.socket()
    
    sk.connect_ex(('127.0.0.1',8888))
    
    msg1 = sk.recv(1024)
    print('msg1:',msg1)
    
    msg2 = sk.recv(1024)
    
    print('msg2:',msg2)
    
    sk.close()
    
    
    #这段代码中可能会出现两个语句合并成一个语句打印出来
    b'halloworld
    • 这种造成数据混乱的现象就叫粘包
    • 粘包现象只用于TCP,UDP永远不会粘包;

    现象:基于tcp协议传输数据时

     

    • 所以只有tcp会发生粘包现象,因为接收端不知道发送端每次发送的数据流是多少,因为数据流没有边界,不能计算
    • 在tcp协议中,有一个合包机制(Nagle算法):针对的是小的数据,每一次send进行打包成一块数据传送,客户端和服务端都会进行三次握手和四次挥手确认信息完整,如果丢失数据,则报错!
    • tcp协议中还有一个拆包,针对的大数据,因为受到网卡的MTU限制,会将大的超过MTU限制的数据,进行拆分,拆分成多个小的数据,进行传输,当传输目标主机的操作系统层时,会重新将多个小的数据合成原本的数据;
    • UDP在实际传输中的过程,基于udp协议时,一个sendto只能对应一个revrfrom,每一个数据相当于带了一个链接地址和内容,这时下一个sendto发送过来的数据和自动接到上一个sendto的后面,所以第一个sendto如果不接收完,就不会接收第二个;
    • 所以以前都是基于udp协议的传输,发送端只管发送,接收端不作出回应,所以一旦中间有个数据丢失,那这个链接的数据就会断开;一个recvfrom(x)必须对唯一一个sendto(y),收完x个字节的数据就算完成,若y>x数据就会丢失,
    • tcp协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,已端总是在收到ack时才会清除缓存区内容,数据是可靠的,但是会粘包

    针对 使用udp协议发送数据,一次收发大小究竟多少合适?

    1)udp不会发生粘包,udp协议本层对一次收发数据大小的限制是

    • 65535 - ip包头(20) - udp包头(8) = 65507

    2)站在数据链路层:

    • 因为网卡的MTU一般被限制在了1500,所以对于数据链路层来说,一次收发数据的大小被限制在: 
    • 1500 - ip包头(20) - udp包头(8) = 1472

    3)得到结论:

    • 如果sendto(num)
    • num > 65507  报错
    •  1472 < num < 65507  会在数据链路层拆包,而udp本身就是不可靠协议,所以一旦拆包之后,造成的多个小数据包在网络传输中,如果丢任何一个,那么此次数据传输失败
    • num < 1472 是比较理想的状态

     

  • 相关阅读:
    Codeforces 691A Fashion in Berland
    HDU 5741 Helter Skelter
    HDU 5735 Born Slippy
    HDU 5739 Fantasia
    HDU 5738 Eureka
    HDU 5734 Acperience
    HDU 5742 It's All In The Mind
    POJ Euro Efficiency 1252
    AtCoder Beginner Contest 067 C
    AtCoder Beginner Contest 067 D
  • 原文地址:https://www.cnblogs.com/0627zhou/p/9470425.html
Copyright © 2011-2022 走看看