博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
最简单的TCP网络封包解包
阅读量:6676 次
发布时间:2019-06-25

本文共 3515 字,大约阅读时间需要 11 分钟。

hot3.png

TCP为什么需要进行封包解包?

        TCP采用字节流的方式,即以字节为单位传输字节序列。那么,我们recv到的就是一串毫无规则的字节流。如果要让这无规则的字节流有规则,那么,就需要我们去定义一个规则。那便是所谓的“封包规则”。

封包结构是怎么样的?

        封包就像是信,信是由:信封、信内容。两部分组成。而网络封包也是由两部分组成:包头、数据。包头域是定长的,数据域是不定长的。包头必然包含两个信息:操作码、包长度。包头可能还包含别的信息,这个呢就要视乎情况去定了。操作码是该网络数据包的标识符,这就和UI里面的事件ID什么的差不多。其中,操作码有的只有一级,有的则有两级甚至多级操作码,这个的设计也要看情况去了,不过,这些底层的东西,定好了,基本就不能动了,就像房子都砌起来了,再去动地基,那就欧也了。
以下是网络数据包的伪代码:
struct NetPacket
{
包头;
数据;
};以下是包头的伪代码:
struct NetPacketHeader
{
操作码;
包长度;
};
收包中存在的一个问题(粘包,半包)
        在现实的网络情况中,网络传输往往是不可靠的,因此会有丢包之类的情况发生,对此,TCP相应的有一个重传的机制。对于接收者来说,它接收到的数据流中的数据有可能不是完整的数据包,或是只有一部分,或是粘着别的数据包,因此,接收者还需要对接收到的数据流的数据进行分包。

服务器客户端逻辑描述

        服务等待一个客户端的连接,客户端连接上了以后,服务器向客户端发送5个数据包,客户端接收服务器端的数据并解包然后做相应的逻辑处理。

需要注意的事项

1.服务器客户端是阻塞的,而不是非阻塞的套接字,这是为了简单;
2.当客户端收到了5个数据包之后,就主动和服务器断开连接,这个是硬代码;
3.阻塞套接字其实没有必要这样处理数据包,主要应用在非阻塞的套接字上。

服务器CPP代码:

#include "stdafx.h"

#include "TCPServer.h"

TCPServer::TCPServer()

: mServerSocket(INVALID_SOCKET)
{
    // 创建套接字
    mServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    if (mServerSocket == INVALID_SOCKET)
    {
        std::cout << "创建套接字失败!" << std::endl;
        return;
    }

    // 填充服务器的IP和端口号

    mServerAddr.sin_family        = AF_INET;
    mServerAddr.sin_addr.s_addr    = INADDR_ANY;
    mServerAddr.sin_port        = htons((u_short)SERVER_PORT);

    // 绑定IP和端口

    if ( ::bind(mServerSocket, (sockaddr*)&mServerAddr, sizeof(mServerAddr)) == SOCKET_ERROR)
    {
        std::cout << "绑定IP和端口失败!" << std::endl;
        return;
    }

    // 监听客户端请求,最大同时连接数设置为10.

    if ( ::listen(mServerSocket, SOMAXCONN) == SOCKET_ERROR)
    {
        std::cout << "监听端口失败!" << std::endl;
        return;
    }

    std::cout << "启动TCP服务器成功!" << std::endl;

}

TCPServer::~TCPServer()

{
    ::closesocket(mServerSocket);
    std::cout << "关闭TCP服务器成功!" << std::endl;
}

void TCPServer::run()

{
    // 接收客户端的连接
    acceptClient();

    int nCount = 0;

    for (;;)
    {
        if (mAcceptSocket == INVALID_SOCKET) 
        {
            std::cout << "客户端主动断开了连接!" << std::endl;
            break;
        }

        // 发送数据包

        NetPacket_Test1 msg;
        msg.nIndex = nCount;
        strncpy(msg.arrMessage, "[1]你好[2]你好[3]你好", sizeof(msg.arrMessage) );
        bool bRet = SendData(NET_TEST1, (const char*)&msg, sizeof(msg));
        if (bRet)
        {
            std::cout << "发送数据成功!" << std::endl;
        }
        else
        {
            std::cout << "发送数据失败!" << std::endl;
            break;
        }

        ++nCount;

    }
}

void TCPServer::closeClient()

{
    // 判断套接字是否有效
    if (mAcceptSocket == INVALID_SOCKET) return;

    // 关闭客户端套接字

    ::closesocket(mAcceptSocket);
    std::cout << "客户端套接字已关闭!" << std::endl;
}

void TCPServer::acceptClient()

{
    // 以阻塞方式,等待接收客户端连接
    int nAcceptAddrLen = sizeof(mAcceptAddr);
    mAcceptSocket = ::accept(mServerSocket, (struct sockaddr*)&mAcceptAddr, &nAcceptAddrLen);
    std::cout << "接受客户端IP:" << inet_ntoa(mAcceptAddr.sin_addr) << std::endl;
}

bool TCPServer::SendData( unsigned short nOpcode, const char* pDataBuffer, const unsigned int& nDataSize )

{
    NetPacketHeader* pHead = (NetPacketHeader*) m_cbSendBuf;
    pHead->wOpcode = nOpcode;

    // 数据封包

    if ( (nDataSize > 0) && (pDataBuffer != 0) )
    {
        CopyMemory(pHead+1, pDataBuffer, nDataSize);
    }

    // 发送消息

    const unsigned short nSendSize = nDataSize + sizeof(NetPacketHeader);
    pHead->wDataSize = nSendSize;
    int ret = ::send(mAcceptSocket, m_cbSendBuf, nSendSize, 0);
    return (ret > 0) ? true : false;
}

客户端CPP代码:

#include "stdafx.h"

#include "TCPClient.h"

TCPClient::TCPClient()
{
    memset( m_cbRecvBuf, 0, sizeof(m_cbRecvBuf) );
    m_nRecvSize = 0;

    // 创建套接字

    mServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    if (mServerSocket == INVALID_SOCKET)
    {
        std::cout << "创建套接字失败!" << std::endl;
        return;
    }

    // 填充服务器的IP和端口号

    mServerAddr.sin_family        = AF_INET;
    mServerAddr.sin_addr.s_addr    = inet_addr(SERVER_IP);
    mServerAddr.sin_port        = htons((u_short)SERVER_PORT);

    //

 

转载于:https://my.oschina.net/mickelfeng/blog/1010600

你可能感兴趣的文章
云HBase发布全文索引服务,轻松应对复杂查询
查看>>
码农张的Bug人生 - 目录
查看>>
学习webpack4 - 抽离公共代码
查看>>
【分享创造】react-typewriter-hook: 用react hooks来实现打字机的效果
查看>>
极限编程 (Extreme Programming) 和用户故事 (User Stories) 的关系
查看>>
coredns 排错记
查看>>
CentOS 7 安装 Nginx
查看>>
程序员毒鸡汤:我们都该学会正确的失败
查看>>
在 JavaScript 中优雅的提取循环内的数据
查看>>
HTML-语义类标签
查看>>
cookie、session、cache-control等
查看>>
一篇文章带你理解闭包
查看>>
Android权限列表
查看>>
Sass基础
查看>>
Webpack3简单入门2
查看>>
Springmvc+mybatis+restful+bootstrap框架整合
查看>>
ubuntu下rsync服务器端和客户端的配置
查看>>
UNIX/Linux 系统管理技术手册阅读(八)
查看>>
LINUX 软件安装。
查看>>
linux 批量文件重命名
查看>>