首页 > 编程语言 >C++ - TCP粘包解决方法

C++ - TCP粘包解决方法

时间:2024-05-28 11:33:34浏览次数:11  
标签:sockAddr MAKEWORD lib int C++ 粘包 TCP include define

 

下面的代码演示了粘包问题,客户端连续三次向服务器端发送数据,服务器端却一次性接收到所有数据。

服务器代码

#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <iostream>
using namespace std;
//#include <stdio.h>
#include <WinSock2.h>​
//
#pragma comment(lib, "ws2_32.lib")


#define BUF_SIZE 100

int main(void)
{
    // 1.初始化套接字库
    WORD wVersion;
    WSADATA wsaData;
    int err;

    // 设置版本,可以理解为1.1
    wVersion = MAKEWORD(1, 1);  // 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来

     // 启动
    err = WSAStartup(wVersion, &wsaData);
    if (err != 0)
    {
        return err;
    }
    // 检查:网络低位不等于1 || 网络高位不等于1
    if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
        // 清理套接字库
        WSACleanup();
        return -1;
    }

    // 2.创建tcp套接字       // AF_INET:ipv4   AF_INET6:ipv6
    SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
    // 准备绑定信息
    SOCKADDR_IN addrSrv;
    addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);   // 设置绑定网卡
    addrSrv.sin_family = AF_INET;       // 设置绑定网络模式
    addrSrv.sin_port = htons(6000);     // 设置绑定端口
    // hton: host to network  x86:小端    网络传输:htons大端

    // 3.绑定到本机
    int retVal = bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
    if (retVal == SOCKET_ERROR)
    {
        cout << "Failed bind:%d\n"<< WSAGetLastError()<<endl;
        return -1;
    }

    // 4.监听,同时能接收10个链接
    if (listen(sockSrv, 10) == SOCKET_ERROR)
    {
        cout << "Listen failed:%d"<<WSAGetLastError()<<endl;
        return -1;
    }

    cout << "Server start at port: 6000" << std::endl;

    SOCKADDR_IN addrCli;
    int len = sizeof(SOCKADDR);

    // 5.接收连接请求,返回针对客户端的套接字
    SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrCli, &len);
    cout << "Accept client IP: " << inet_ntoa(addrCli.sin_addr) << std::endl;
    if (sockConn == SOCKET_ERROR)
    {
        cout << "Accept failed: " << WSAGetLastError() << std::endl;
        return -1;
    }

    char recvBuf[BUF_SIZE];
    char sendBuf[BUF_SIZE];

	// 7.接收数据
	recv(sockConn, recvBuf, BUF_SIZE, 0);
	cout << "客户端:" << recvBuf << std::endl;

    // 8.关闭套接字
    closesocket(sockSrv);
    // 9.清理套接字库
    WSACleanup();
    return 0;
}


//#include <stdio.h>
//#include <windows.h>
//#pragma comment (lib, "ws2_32.lib")  //加载 ws2_32.dll
//
//#define BUF_SIZE 100
//
//int main() 
//{
//    WSADATA wsaData;
//    WSAStartup(MAKEWORD(2, 2), &wsaData);
//
//    //创建套接字
//    SOCKET servSock = socket(AF_INET, SOCK_STREAM, 0);
//
//    //绑定套接字
//    sockaddr_in sockAddr;
//    memset(&sockAddr, 0, sizeof(sockAddr));  //每个字节都用0填充
//    sockAddr.sin_family = PF_INET;  //使用IPv4地址
//    sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址
//    sockAddr.sin_port = htons(1234);  //端口
//    bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
//
//    //进入监听状态
//    if(listen(servSock, 20) == SOCKET_ERROR)
//    {
//        cout << "Listen failed:%d" << WSAGetLastError() << endl;
//        return -1;
//    }
//    cout << "Server start at port: 1234" << std::endl;
//
//    //接收客户端请求
//    SOCKADDR_IN clntAddr;
//    int nSize = sizeof(SOCKADDR);
//    char buffer[BUF_SIZE] = { 0 };  //缓冲区
//    SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize);
//    cout << "Accept client IP: " << inet_ntoa(clntAddr.sin_addr) << std::endl;
//    if (clntSock == SOCKET_ERROR)
//    {
//        cout << "Accept failed: " << WSAGetLastError() << std::endl;
//        return -1;
//    }
//
//    Sleep(10000);  //注意这里,让程序暂停10秒
//
//    //接收客户端发来的数据,并原样返回
//    int recvLen = recv(clntSock, buffer, BUF_SIZE, 0);
//    printf("recv buffer: %s", buffer);
//    send(clntSock, buffer, recvLen, 0);
//
//    //关闭套接字并终止DLL的使用
//    closesocket(clntSock);
//    closesocket(servSock);
//    WSACleanup();
//
//    return 0;
//}

客户端代码

#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <iostream>
using namespace std;
#include <WinSock2.h>
#include <stdio.h>
#include <string.h>

#pragma comment(lib, "ws2_32.lib")

#define BUF_SIZE 100

int main(void)
{
    // 1.初始化套接字库
    WORD wVersion;
    WSADATA wsaData;
    int err;

    // 可以理解为1.1
    wVersion = MAKEWORD(1, 1);  // 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来
    // 启动

    err = WSAStartup(wVersion, &wsaData);
    if (err != 0)
    {
        return err;
    }
    // 检查:网络地位不等于1 || 网络高位不等于1
    if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
        // 清理套接字库
        WSACleanup();
        return -1;
    }

    // 创建TCP套接字
    SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);
    SOCKADDR_IN addrSrv;
    addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");  // 服务器地址
    addrSrv.sin_port = htons(6000);     // 端口号
    addrSrv.sin_family = AF_INET;       // 地址类型(ipv4)

    // 2.连接服务器
    int err_log = connect(sockCli, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
    if (err_log == 0)
    {
        cout << "连接服务器成功!" << endl;
    }
    else
    {
        cout << "连接服务器失败!" << endl;
        return -1;
    }

    char recvBuf[BUF_SIZE];
    char sendBuf1[BUF_SIZE] = "data1";
    char sendBuf2[BUF_SIZE] = "data2";
    char sendBuf3[BUF_SIZE] = "data3";

	// 3.发送数据
	send(sockCli, sendBuf1, strlen(sendBuf1), 0);
	send(sockCli, sendBuf2, strlen(sendBuf2), 0);
	send(sockCli, sendBuf3, strlen(sendBuf3), 0);


    // 5.关闭套接字并清除套接字库
    closesocket(sockCli);
    WSACleanup();

    system("pause");
    return 0;
}



//#define BUF_SIZE 100
//
//int main() 
//{
//    //初始化DLL
//    WSADATA wsaData;
//    WSAStartup(MAKEWORD(2, 2), &wsaData);
//
//    //向服务器发起请求
//    sockaddr_in sockAddr;
//    memset(&sockAddr, 0, sizeof(sockAddr));  //每个字节都用0填充
//    sockAddr.sin_family = PF_INET;
//    sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
//    sockAddr.sin_port = htons(1234);
//
//    //创建套接字
//    SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
//    connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
//
//    //获取用户输入的字符串并发送给服务器
//    char bufSend[BUF_SIZE] = { 0 };
//    //printf("Input a string: ");
//    //gets_s(bufSend);
//    sprintf_s(bufSend, "你好!");
//    for (int i = 0; i < 3; i++) 
//    {
//        send(sock, bufSend, strlen(bufSend), 0);
//    }
//    //接收服务器传回的数据
//    char bufRecv[BUF_SIZE] = { 0 };
//    recv(sock, bufRecv, BUF_SIZE, 0);
//    //输出接收到的数据
//    printf("Message form server: %s\n", bufRecv);
//
//    closesocket(sock);  //关闭套接字
//    WSACleanup();  //终止使用 DLL
//
//    system("pause");
//    return 0;
//}

执行结果:

 

标签:sockAddr,MAKEWORD,lib,int,C++,粘包,TCP,include,define
From: https://www.cnblogs.com/zhuchunlin/p/18217567

相关文章

  • C++中的异类:“#” 符号背后的故事
    最近在写编程语言的书,聊到C++的宏,感觉很有意思,搬运过来。在C++语言中,# 符号是一个独特的符号。它似乎不在语言核心中,但是在源码里却又无处不在。在语法上,#的语法规则在C++体系里独具一格,和C++语法相比像是两个语言似的。这些差别让我们感受到#背后的故事不简单。今天,我们......
  • c++函数指针
     c/c++函数指针的用法【目录】基本定义c函数指针使用举例c++函数指针使用举例函数指针作为函数参数函数指针作为函数返回值函数指针数组typedef简化函数指针操作 c语言函数指针的定义形式:返回类型 (*函数指针名称)(参数类型,参数类型,参数类型,…);c++函数指针......
  • 一、TCP/IP协议
    学习自######https://xiaolincoding.com/network/1_base/tcp_ip_model.html#%E5%BA%94%E7%94%A8%E5%B1%82前提同一设备间的通信:管道、消息队列、共享内容、信号等方式不同设备间的通信:通用的网络协议来兼容各种设备网络协议是分层的:应用层、传输层、网络层、网络接口层应......
  • TCP滑动窗口
    发送方发送报文不再使用一个一个报文发送然后等待一个一个确认,而是进行一段(多个报文)发送接收方接收到数据后,发送当前接收到数据序列值+1,以及下一次可以接收的窗口值 也就是说,发送方需要配合接收方接受的窗口大小来确定数值发送 发送方窗口左边为后沿,右边为前沿。1.当......
  • lambda表达式的用例 c++
    出自:  https://blog.csdn.net/qq_45604814/article/details/132687858一、Lambda表达式概述1.介绍Lambda表达式是C++11标准引入的一种特性,它提供了一种方便的方式来定义匿名函数。Lambda表达式是一种能够捕捉外部变量并使用它们的函数对象。由捕获列表、参数列表、返......
  • 打开编程世界 跟着Mr.狠人一起学C/C++
    打开编程世界跟着Mr.狠人一起学C/C++自我介绍大家好,我是Mr.狠人。我高中就读于墨尔本,学习的方向是会计,因为疫情我回到国内读大学,大学的专业是国贸,可以说我没有任何的计算机基础。但我在海外研究生阶段毅然决然的选择了计算机专业,我本可以选择金融专业,或是更简单的管理专业......
  • C++ ─── string的模拟实现
            本博客将简单实现来模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数。    下期我们继续讲解完整版string的模拟实现(将不再会是浅拷贝了)        说明:下述string类没有显式定义其拷贝构造函数与赋值运......
  • A Simple Problem with Integers(C++)
     【题目描述】这是一道模板题。给定数列 a[1],a[2],…,a[n] ,你需要依次进行q 个操作,操作有两类:C、lrx :给定 l,r,x ,对于所有 i∈[l,r] ,将 a[i] 加上 x (换言之,将 a[l],a[l+1],…,a[r] 分别加上 x );Q、lr :给定l,r ,求 ∑ri=la[i] 的值(换言之,求 a[l]+a[l+......
  • c++设计模式-装饰器模式和代理模式
    namespace_nmsp1{//抽象的控件类classControl{public:virtualvoiddraw()=0;//draw方法,用于将自身绘制到屏幕上。public:virtual~Control(){}//做父类时析构函数应该为虚函数};//列表控件类classListCtrl......
  • [Java EE] 网络编程与通信原理(三):网络编程Socket套接字(TCP协议)
    ......