• TCP、UDP通信


    开放系统互连参考模型 (Open System Interconnect 简称OSI)

    OSI七层模型

    1.应用层
    2.表示层
    3.会话层
    4.传输层
    5.网络层
    6.数据链路层
    7.物理层

    TCP/IP模型
    1.应用层 上面3层:应用程序、协议:HTTP、FTP
    2.传输层 TCP UDP
    3.网络层 IP(不可靠) ARP RARP ICMP
    4.数据链路层 下面2层

    UDP User Datagram Protocol
    无连接的传送层协议,提供不可靠的信息传输服务


    1.提供无连接服务,客户端向服务器发送数据时不必先建立连接,客户端创建一个套接字并向服务器发送一个数据,然后客户端可立即用这个套接字向另外一个服务器发送其他数据。
    2.不能确保UDP数据报最终到达目的地,UDP对接收的数据不发送确认,发送端不知道数据是否被正确接收,也不会重发数据。
    3.UDP传输数据较TCP快,占用资源少。
    UDP信息包的标题很短,只有8个字节,相对于TCP的20个字节信息包的额外开销很小。
    UDP由于排除了信息可靠传递机制,将安全和排序等功能移交给上层应用来完成,极大降低了执行时间,使速度得到了保证。

    TCP Transmission Control Protocol

    面向连接的、可靠的、基于字节流的传送层通信协议。

    1.提供面向连接的服务。客户端与服务端通信时,必须首先建立连接。
    2.提供可靠的服务。当TCP向对方发送数据时,要求对方返回一个确认,如果没有接收到对方的确认,则TCP自动重传数据。
    3.TCP对发送的数据进行排序,为每个发送字节关联一个序列号。对方根据接收到的数据序列号,对接收数据排序,从而保证数据顺序
    3.TCP提供流量控制,TCP总是告知对方它能够接收数据的字节数。
    4.TCP连接是全双工的,这意味着应用程序在任何时候,既可以发送数据也可以接收数据。


    连接:
    三次握手
    1.服务器准备接收客户端的连接。
    2.客户端向服务器发起请求,此时客户端TCP发送一个SYN分节。
    3.服务器确认客服端的SYN(同步),同时也发送一个SYN分节,服务器以单个分节向客户端发送SYN和对客户端SYN的ACK(确认)。
    4.客户端确认服务器的SYN。

    第一次握手:Client什么都不能确认;Server确认了对方发送正常
    第二次握手:Client确认了:自己发送、接收正常,对方发送、接收正常;Server确认了:自己接收正常,对方发送正常
    第三次握手:Client确认了:自己发送、接收正常,对方发送、接收正常;Server确认了:自己发送、接收正常,对方发送接收正常

    断开连接:
    四次挥手

    问题1: 为什么要四次挥手?

    答:根本原因是,一方发送FIN只表示自己发完了所有要发的数据,但还允许对方继续把没发完的数据发过来。

    举个例子:A和B打电话,通话即将结束后,A说“我没啥要说的了”,B回答“我知道了”,但是B可能还会有要说的话,
    A不能要求B跟着自己的节奏结束通话,于是B可能又巴拉巴拉说了一通,最后B说“我说完了”,A回答“知道了”,这样通话才算结束。


    问题2:为什么双方要发送这样的数据包?

    答:和握手的情况类似,只是为了让对方知晓自己理解了对方的意图。

    由于主机IP地址与网络服务是一对多的关系,所以主机使用不同的端口号区分不同的网络服务
    0-65535
    通用端口号:0-1023 紧密绑定某些特殊服务,80是HTTP通信端口,21是FTP通信端口
    已注册端口号:1024-49151,提供一般应用程序使用
    动态或私有端口:49162-65535

    报文:
    报文(message)是网络中交换与传输的数据单元,即站点一次性要发送的数据块。
    报文包含了将要发送的完整的数据信息,其长短很不一致,长度不限且可变。

    报文也是网络传输的单位,传输过程中会不断的封装成分组、包、帧来传输,
    封装的方式就是添加一些信息段,那些就是报文头以一定格式组织起来的数据。

    IP数据包:20字节IP包头+9字节UDP包头+UDP数据

    UDP报头由4个域组成,其中每个域各占用2个字节,共8个字节

    16位源端口号 16位目的端口号
    16位UDP长度 16位UDP校验和
    数据(如果有)
    ......

    数据包的长度是指包括报头和数据部分在内的总字节数,报头的长度是固定的
    包含报头在内的数据报的最大长度为65535字节。不过,一些实际应用往往会限制数据报的大小,有时会降低到8192字节。

    UDP协议使用报头中的校验值来保证数据的安全。
    校验值首先在数据发送方通过特殊的算法计算得出,在传递到接收方之后,还需要再重新计算。
    检测到错误时,UDP不做错误校正,只是简单地把损坏的消息段扔掉,或者给应用程序提供警告信息。


    UDP不排序,到达顺序也可能跟发送时的顺序不同。

    UDP:
        WSADATA wsaData;
        int ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
        if (ret != 0)
        {
            WSACleanup();
            return 0;
        }
        MFC为
        if (!AfxSocketInit())
        {
            AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
            return FALSE;
        }
    
    服务端:
        1.创建数据报套接字
          SOCKET severSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        2.绑定端口
          SOCKADDR_IN recvAddr;
          recvAddr.sin_family = AF_INET;
          recvAddr.sin_port = htons(9001);  //本地(服务器端口),客户端发送到这个端口
          recvAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);  //接收任何IP的消息
          if (bind(severSocket, (SOCKADDR*)&recvAddr, sizeof(recvAddr)) == SOCKET_ERROR)
          {
              closesocket(severSocket);
              WSACleanup();
              return 0;
          }
          
          3.收发数据
          
          SOCKADDR_IN sendAddr;
          int nSendAddrSize = sizeof(sendAddr);
          char recvBuf[1024] = { 0 };
          //会接收到对方的IP和端口,发送的时候使用这个IP和端口
          recvfrom(severSocket, recvBuf, sizeof(recvBuf), 0, (SOCKADDR*)&sendAddr, &nSendAddrSize)  
          sendto(severSocket, recvBuf, sizeof(recvBuf), 0, (SOCKADDR*)&sendAddr, nSendAddrSize);
          
          4.关闭套接字
          closesocket(severSocket);
          WSACleanup();
          
    客户端://未绑定端口,系统自动分配端口,这样对方在没有收到你的消息之前不知道你的端口号,不能向你发数据
            //可以绑定一个端口
        1.创建数据报套接字
          SOCKET clientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        
        2.收发数据
          SOCKADDR_IN sendAddr;
          sendAddr.sin_family = AF_INET;
          sendAddr.sin_port = htons(9001);  //对方端口
          sendAddr.sin_addr.S_un.S_addr = inet_addr("192.168.0.126"); // //对方IP 
          
          char sendBuf[1024] = { 0 };
          sendto(clientSocket, sendBuf, sizeof(sendBuf), 0, (SOCKADDR*)&sendAddr, sizeof(sendAddr));
          
          char recvBuf[1024] = { 0 };
          SOCKADDR_IN recvAddr;
           int nRecvAddrSize = sizeof(SOCKADDR_IN);
          recvfrom(clientSocket, recvBuf, 1024, 0, (SOCKADDR*)&recvAddr, &nRecvAddrSize);
    
        3.关闭套接字
          closesocket(clientSocket);
          WSACleanup();
          
          
        MFC: CAsyncSocket或者CSocket
        //nLocalPort为0,则系统自动分配端口,这样对方在没有收到你的消息之前不知道你的端口号,不能向你发数据
        Create(nLocalPort, SOCK_DGRAM);    //创建及绑定端口(封装了bind),不要再调用Bind了
        
        SendTo(str, str.GetLength(), unPort, strIP);//对方端口、IP
    
        OnReceive:
        char recvBuf[1024] = { 0 };
        CString strIP = _T("");
        UINT nPort = 0;
        ReceiveFrom(recvBuf, 1024, strIP, nPort);//收了发送方的数据、端口及IP
        
        Close();

    TCP:

    服务端:

      创建套接字--绑定--监听--accept接受客户端连接--收发数据--关闭套接字

    客户端:

      创建套接字--connect--收发数据--关闭套接字

  • 相关阅读:
    初学python遇到的第一个坑
    返回列表中最长的连续字符串
    输入一个数字,求每一位相加之和
    判断一个数是否为素数
    编写一个函数,它接受一个或多个单词的字符串,并返回相同的字符串,但所有五个或多个字母的单词都颠倒过来
    判断10步能不能回到原点
    完成方法/函数,以便将破折号/下划线分隔的单词转换为驼峰式大小写
    求公共汽车上的人数
    写一个函数,返回不同的计数
    对一个数的每一位数字求平方
  • 原文地址:https://www.cnblogs.com/xslwm/p/10604105.html
Copyright © 2020-2023  润新知