web - socket 数据粘包处理

通信类

tcpSocket.h

/*
    通信类
*/
#ifndef TCPSOCKET_H
#define TCPSOCKET_H

#include <iostream>
#include <string>
#include <sys/socket.h>
#include <unistd.h>
#include <cstdlib>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>

using namespace std;

class TCPSocket
{
private:
    int m_fd;
    
    int readn(char *buf,int size);
    int writen(const char *msg,int size);
public:
    TCPSocket(/* args */);
    TCPSocket(int sock);
    ~TCPSocket();

    int connectToHost(string ip,unsigned short port);

    int sendMsg(string msg);

    string recvMsg();
};


#endif

tcpSocket.cpp

/*
    通信类
*/

#include "tcpSocket.h"

TCPSocket::TCPSocket(/* args */)
{
    m_fd=socket(AF_INET,SOCK_STREAM,0); // 创建socket
}

TCPSocket::TCPSocket(int sock)
{
    m_fd=sock; // 创建socket
}

TCPSocket::~TCPSocket()
{
    if (m_fd>0)
    {
        close(m_fd);
    }
}

int TCPSocket::connectToHost(string ip,unsigned short port)
{
    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof(sockaddr_in));
    saddr.sin_family=AF_INET;
    saddr.sin_port=htons(port);
    saddr.sin_addr.s_addr=inet_addr(ip.data());

    int ret=connect(m_fd,(struct sockaddr*)&saddr,sizeof(saddr));
    if (ret==-1)
    {
        perror("connect");
        return -1;
    }

    cout<<"conncet server 成功..."<<endl;

    return ret;
}

int TCPSocket::sendMsg(string msg)
{
    // 申请内存空间:数据长度+包头4字节(存储数据长度)
    char *data=new char[msg.size()+4];
    int bigLen=htonl(msg.size()); // h -> n 将long类型数据从主机字节序转换为网络字节序

    memcpy(data,&bigLen,4); // !!!
    memcpy(data+4,msg.data(),msg.size()); // 字节流

    // 发送数据
    int ret=writen(data,msg.size()+4);
    delete[] data; // 必须是delete[]
    return ret;
}

string TCPSocket::recvMsg()
{
    // 接收数据
    // 读取数据头
    int len=0;
    readn((char*)&len,4); 

    len=ntohl(len); // 网络字节序列转换为本机字节序
    // cout<<"数据块的大小: "<<len<<endl;


    // 根据提取出的长度分配内存
    char *buf=new char[len+1];
    int ret=readn(buf,len);
    if (ret!=len)
    {
        return string(); // 返回空字符串
    }

    buf[len]='\0'; // 添加结束符
    string retStr(buf); // 创建字符串对象,调用构造函数
    delete[] buf;

    return retStr;
}


int TCPSocket::readn(char *buf,int size)
{
    int nread=0;
    int left=size;

    char *p=buf;

    while(left>0)
    {
        if ((nread=read(m_fd,p,left))>0)
        {
            p+=nread;
            left-=nread;
        }
        else if(nread==-1)
        {
            return -1;
        }
    }

    return size;

}

int TCPSocket::writen(const char *msg,int size)
{
    int left=size;
    int nwrite=0;

    const char*p=msg;

    while (left>0)
    {
        if ((nwrite=write(m_fd,msg,left))>0)
        {
            p+=nwrite;
            left-=nwrite;
        }
        else if (nwrite==-1)
        {
            return -1;
        }
    }

    return size;
}

服务类

tcpServer.h

/*
    服务类
*/
#ifndef TCPSERVER_H
#define TCPSERVER_H

#include <iostream>
#include <string>
#include <sys/socket.h>
#include <unistd.h>
#include <cstdlib>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "tcpSocket.h"

class TCPServer
{
private:
    int m_fd;
public:
    TCPServer(/* args */);
    ~TCPServer();

    int setListen(string ip,unsigned short port);
    TCPSocket * acceptConn(struct sockaddr_in * addr=nullptr);
};

#endif

tcpServer.cpp

/*
    服务类
*/

#include "tcpServer.h"

TCPServer::TCPServer(/* args */)
{
    m_fd=socket(AF_INET,SOCK_STREAM,0);
}

TCPServer::~TCPServer()
{
    close(m_fd);
}


int TCPServer::setListen(string ip,unsigned short port)
{
    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof(sockaddr_in));
    saddr.sin_family=AF_INET;
    saddr.sin_port=htons(port);
    saddr.sin_addr.s_addr=inet_addr(ip.data());

    int ret=bind(m_fd,(struct sockaddr*)&saddr,sizeof(saddr));
    if (ret==-1)
    {
        perror("bind");
        return -1;
    }

    cout<<"bind 成功..."<<endl;
    cout<<"ip: "<<inet_ntoa(saddr.sin_addr)<<" port: "<<port<<endl;

    ret=listen(m_fd,128);
    if (ret==-1)
    {
        perror("listen");
        return -1;
    }

    cout<<"设置listen 成功..."<<endl;

    return ret;
}

TCPSocket * TCPServer::acceptConn(struct sockaddr_in * addr)
{
    if (addr==NULL)
    {
        return nullptr;
    }

    socklen_t addrlen=sizeof(struct sockaddr_in);
    int cfd=accept(m_fd,(struct sockaddr*)addr,&addrlen);
    if (cfd==-1)
    {
        perror("accept");
        return nullptr;
    }

    cout<<"connect 客户端 成功..."<<endl;

    return new TCPSocket(cfd);
}

客户端

client.cpp

/*
    客户端
*/
#include <iostream>
#include <fcntl.h>
#include "tcpSocket.h"

int main()
{
    // 创建通信类
    TCPSocket tcp;

    int ret=tcp.connectToHost("127.0.0.1",8080);
    if (ret==-1)
    {
        return -1;
    }

    // 通信
    int fd1=open("test.txt",O_RDONLY);
    int length=0;
    char tmp[100];

    memset(tmp,0,sizeof(tmp));

    // 每次读取固定大小的字节数据
    cout<<"send msg"<<endl;
    while((length=read(fd1,tmp,sizeof(tmp)))>0)
    {
        // 发送数据
        tcp.sendMsg(string(tmp,length));

        // cout<<"send msg"<<endl;
        cout<<tmp;
        memset(tmp,0,sizeof(tmp));

        // 接收数据
        usleep(300);
    }
    
    sleep(10);

    return 0;
}

服务端

server.cpp

/*
    服务端
*/

#include <iostream>
#include <fcntl.h>
#include <pthread.h>

#include "tcpSocket.h"
#include "tcpServer.h"

using namespace std;

struct SockInfo
{
    TCPServer *s;
    TCPSocket *tcp;

    struct sockaddr_in addr;
};

void *working(void *arg)
{
    struct SockInfo *pinfo=static_cast<struct SockInfo*>(arg);

    // 连接建立成功,打印客户端的IP和端口信息
    char ip[32];
    // inet_ntop 用于ipv4/ipv6
    // inet_ntoa 用于ipv4
    cout<<"ip: "<<inet_ntop(AF_INET,&(pinfo->addr.sin_addr.s_addr),ip,sizeof(ip));
    // 网络字节序转换为主机字节序列
    cout<<" port: "<<ntohs(pinfo->addr.sin_port);


    // 通讯
    cout<<"接收数据..."<<endl;
    while (1)
    {
        // cout<<"接收数据..."<<endl;

        string msg=pinfo->tcp->recvMsg();

        if (!msg.empty())
        {
            cout<<msg;
        }
        else
        {
            break;
        }
    }

    delete pinfo->tcp;
    delete pinfo;

    return nullptr;
}


/*
    一个服务器监听多个客户端
*/
int main()
{
    // 创建监听套接字
    TCPServer s;

    // 绑定端口
    s.setListen("127.0.0.1",8080);

    while (1)
    {
        SockInfo *info=new SockInfo;

        // 返回一个新的通讯类tcp
        TCPSocket *tcp=s.acceptConn(&info->addr);

        if (tcp==nullptr)
        {
            cout<<"重试..."<<endl;
            continue;
        }


        // 创建子线程
        pthread_t tid;

        info->s=&s;
        info->tcp=tcp;

        pthread_create(&tid,NULL,working,info);
        pthread_detach(tid);
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值