TCP网络传输-粘包问题解决

该博客详细介绍了如何在C++中实现TCP/IP通信,通过先发送包长度再发送包内容的方式来确保数据完整传输。服务器端首先加载Winsock库,创建并绑定socket,然后监听连接请求。当有客户端连接时,服务器创建新线程进行通信。客户端则负责连接服务器,输入消息并发送,同时接收服务器的响应。整个过程展示了TCP连接的基本流程和数据传输策略。
摘要由CSDN通过智能技术生成

解决方案: 先发送包长度,再发送包大小

server.cpp

#include <QCoreApplication>
#include <winsock.h>
#include <windows.h>
#include <iostream>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
DWORD WINAPI ThreadFun(LPVOID lpThreadParameter);

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    //1.加载库
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    wVersionRequested = MAKEWORD(2, 2);

    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {

        printf("WSAStartup failed with error: %d\n", err);
        return 1;
    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        printf("Could not find a usable version of Winsock.dll\n");
        WSACleanup();
        return 1;
    }
    else
        printf("The ServerWinsock 2.2 dll was found okay\n");
    //2.创建socket
    SOCKET sock = socket(AF_INET,SOCK_STREAM,0);
    if (sock == INVALID_SOCKET)
    {
        WSACleanup();
        return 1;
    }
    //3.绑定
    sockaddr_in addrserver;
    addrserver.sin_family = AF_INET;
    addrserver.sin_port = htons(8899);
    addrserver.sin_addr.S_un.S_addr=0;
    if (SOCKET_ERROR == bind(sock,(const sockaddr*)&addrserver,sizeof(addrserver)))
    {
        closesocket(sock);
        WSACleanup();
        return 1;
    }
    //4.监听事件
    //listen只是把套接字从主动变为被动,并限制链接数
    if (SOCKET_ERROR == listen(sock,10))//10:服务器进程不能随便指定一个数值,内核有许可的范围
    {
        closesocket(sock);
        WSACleanup();
        return 1;
    }
    //5.拉着客人进店 ,将客人交给服务员
    //SOCKET sockWaiter = accept(sock,0,0);//第一个参数指定处于监听状态的流套接字 第二个参数来返回新创建的套接字的地址结构  第三个参数来返回新创建套接字的地址结构的长度。
    while(1)
    {
        sockaddr_in addrclient;
        int len = sizeof (addrclient);
        //接收成功返回与client通讯的socket   //阻塞
        SOCKET sockWaiter = accept(sock,(sockaddr*)&addrclient,&len);
        if (sockWaiter != INVALID_SOCKET)
        {
            //创建线程 并且传入与client通讯的套接字
            HANDLE hThread =CreateThread(0,0, &ThreadFun,(LPVOID)sockWaiter,0,0);
            CloseHandle(hThread);
        }
    }
    closesocket(sock);
    WSACleanup();
    return a.exec();
}
//线程通讯部分
DWORD WINAPI ThreadFun(LPVOID lpThreadParameter)
{
    SOCKET sockWaiter = (SOCKET)lpThreadParameter;
    while(1){
    //6.接收消息 -- recv();
    /*
     * s<r normal;
     * s>r 分次接
    */
        char *pszbuf = NULL;//定义指针
        int nRecNum;
        int nPacksize;
        int noffset;//偏移量
        while(1)
        {
            noffset = 0;
            //1.接收包大小 nRecNum代表接收的字节  例如:szbuf[100] 包大小100占用4个字节
            nRecNum = recv(sockWaiter,(char*)&nPacksize,sizeof(int),0);
            if (nRecNum <= 0) continue;
            //2.包内容
            pszbuf = new char[nPacksize];//创建nPacksize大小的空间
    
            while(nPacksize) //当分次接收的包大小为0时,结束循环
            {
                nRecNum = recv(sockWaiter,pszbuf+noffset,nPacksize,0);
                noffset += nRecNum;
                nPacksize -= nRecNum;
            }
            cout<<pszbuf<<endl;
        }
    }
}

client.cpp

#include <QCoreApplication>
#include <winsock.h>
#include <iostream>
using namespace std;
#pragma comment(lib, "ws2_32.lib")

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    //加载库
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    wVersionRequested = MAKEWORD(2, 2);

    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {

        printf("WSAStartup failed with error: %d\n", err);
        return 1;
    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        printf("Could not find a usable version of Winsock.dll\n");
        WSACleanup();
        return 1;
    }
    else
        printf("The ClientWinsock 2.2 dll was found okay\n");
    //创建socket
    SOCKET sock = socket(AF_INET,SOCK_STREAM,0);
    if (sock == INVALID_SOCKET)
    {
        WSACleanup();
        return 1;
    }
    //链接
    sockaddr_in addrserver;
    addrserver.sin_family = AF_INET;
    addrserver.sin_port = htons(8899);
    //inet_pton(AF_INET, "127.0.0.1", &addrserver.sin_addr);
    addrserver.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
    if (SOCKET_ERROR == connect(sock,(const sockaddr*)&addrserver,sizeof (addrserver)))
    {
        closesocket(sock);
        WSACleanup();
        return 1;
    }
    while(1){
        char szbuf[1024] = {0};
        cin>>szbuf;
        //发送
        //1.发送包长度
        int nPackSize = sizeof(szbuf);
        send(sock,(const char*)&nPackSize,sizeof(int),0);//发送一块缓冲区,所以第二个参数就是(const char*)&nPackSize 第三个参数:代表第二个参数的大小 第二个参数是int类型,所以是sizeof(int)
        //2.发送包内容
        send(sock,szbuf,sizeof(szbuf),0);
        //接收
        int RecvNum = recv(sock,szbuf,sizeof (szbuf),0);
        if (RecvNum > 0)
        {
           cout<<szbuf<<endl;
        }
    }
    //关闭socket
    closesocket(sock);
    //卸载库
    WSACleanup();

    return a.exec();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值