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 是比较理想的状态

     

  • 相关阅读:
    前端代码规范
    使用CSS设置行间距,字间距.
    今天我们要说的画一个三角形,恩,画一个三角形,第一种呢是利用我们的html标签结合css来实现;而第二种方法就就是我们的html5新增的一个标签canves,这个canves就是网页画幕,那么顾名思义就是在网页里建造一个画板,用来画画,好,那接下来就和我一起去看看吧!
    javascript基础程序(算出一个数的平方值、算出一个数的阶乘、输出!- !- !- !- !- -! -! -! -! -! 、函数三个数中的最大数)
    css3 风车旋转
    css3简单的图片轮播
    css3 2D动画的基本用法和介绍
    Android 系统UI设计规则
    Android用户界面程序设计示例
    将博客搬至CSDN
  • 原文地址:https://www.cnblogs.com/0627zhou/p/9470425.html
Copyright © 2011-2022 走看看