TCP/IP 网络数据封包和解包
2014-01-06 11:48
477 查看
TCP/IP 网络数据以流的方式传输,数据流是由包组成,如何判定接收方收到的包是否是一个完整的包就要在发送时对包进行处理,这就是封包技术,将包处理成包头,包体
包头是包的开始标记,整个包的大小就是包的结束标记。接收方只要按同样的方式解包即可,下面是我在网上搜罗的一个网络服务端和客户端程序代码。
客户端和服务端共享的文件:(数据包的定义)
服务端:
客户端:
包头是包的开始标记,整个包的大小就是包的结束标记。接收方只要按同样的方式解包即可,下面是我在网上搜罗的一个网络服务端和客户端程序代码。
客户端和服务端共享的文件:(数据包的定义)
#pragma once #define NET_PACKET_DATA_SIZE 1024 #define NET_PACKET_SIZE (sizeof(NetPacketHeader) + NET_PACKET_DATA_SIZE) * 10 /// 网络数据包包头 struct NetPacketHeader { unsigned short wDataSize; ///< 数据包大小,包含封包头和封包数据大小 unsigned short wOpcode; ///< 操作码 }; /// 网络数据包 struct NetPacket { NetPacketHeader Header; ///< 包头 unsigned char Data[NET_PACKET_DATA_SIZE]; ///< 数据 }; ////////////////////////////////////////////////////////////////////////// /// 网络操作码 enum eNetOpcode { NET_TEST1 = 1, }; /// 测试1的网络数据包定义 struct NetPacket_Test1 { int nIndex; char name[20]; char sex[20]; int age; char arrMessage[512]; };
服务端:
#pragma once class TCPServer { public: TCPServer(); virtual ~TCPServer(); public: void run(); /// 接受客户端接入 void acceptClient(); /// 关闭客户端 void closeClient(); /// 发送数据 bool SendData(unsigned short nOpcode, const char* pDataBuffer, const unsigned int& nDataSize); private: SOCKET mServerSocket; ///< 服务器套接字句柄 sockaddr_in mServerAddr; ///< 服务器地址 SOCKET mAcceptSocket; ///< 接受的客户端套接字句柄 sockaddr_in mAcceptAddr; ///< 接收的客户端地址 char m_cbSendBuf[NET_PACKET_SIZE]; };
#include "stdafx.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; msg.age=23; strncpy(msg.arrMessage, "北京市朝阳区", sizeof(msg.arrMessage) ); strncpy(msg.name, "天策", sizeof(msg.name) ); strncpy(msg.sex, "男", sizeof(msg.sex) ); 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; }
// testTCPServer.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" int _tmain(int argc, _TCHAR* argv[]) { TCPServer server; server.run(); system("pause"); return 0; }
客户端:
#pragma once class TCPClient { public: TCPClient(); virtual ~TCPClient(); public: /// 主循环 void run(); /// 处理网络消息 bool OnNetMessage(const unsigned short& nOpcode, const char* pDataBuffer, unsigned short nDataSize); bool OnNetPacket(NetPacket_Test1* pMsg); private: SOCKET mServerSocket; ///< 服务器套接字句柄 sockaddr_in mServerAddr; ///< 服务器地址 char m_cbRecvBuf[NET_PACKET_SIZE]; char m_cbDataBuf[NET_PACKET_SIZE]; int m_nRecvSize; };
#include "stdafx.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); // 连接到服务器 if ( ::connect(mServerSocket, (struct sockaddr*)&mServerAddr, sizeof(mServerAddr))) { ::closesocket(mServerSocket); std::cout << "连接服务器失败!" << std::endl; return; } } TCPClient::~TCPClient() { ::closesocket(mServerSocket); } void TCPClient::run() { int nCount = 0; for (;;) { // 接收数据 int nRecvSize = ::recv(mServerSocket, m_cbRecvBuf+m_nRecvSize, sizeof(m_cbRecvBuf)-m_nRecvSize, 0); if (nRecvSize <= 0) { std::cout << "服务器主动断开连接!" << std::endl; break; } // 保存已经接收数据的大小 m_nRecvSize += nRecvSize; // 接收到的数据够不够一个包头的长度 while (m_nRecvSize >= sizeof(NetPacketHeader))//已经收到一个完整的包,如果没用收到一个完整的包,此处循环不执行,继续下一轮循环 { // 收够5个包,主动与服务器断开 if (nCount >= 5) { ::closesocket(mServerSocket); break; } // 读取包头 NetPacketHeader* pHead = (NetPacketHeader*) (m_cbRecvBuf); const unsigned short nPacketSize = pHead->wDataSize; // 判断是否已接收到足够一个完整包的数据 if (m_nRecvSize < nPacketSize) { // 还不够拼凑出一个完整包 break; } // 拷贝到数据缓存 CopyMemory(m_cbDataBuf, m_cbRecvBuf, nPacketSize); // 从接收缓存移除 MoveMemory(m_cbRecvBuf, m_cbRecvBuf+nPacketSize, m_nRecvSize); m_nRecvSize -= nPacketSize; // 解密数据,以下省略一万字 // ... // 分派数据包,让应用层进行逻辑处理 pHead = (NetPacketHeader*) (m_cbDataBuf); const unsigned short nDataSize = nPacketSize - (unsigned short)sizeof(NetPacketHeader); OnNetMessage(pHead->wOpcode, m_cbDataBuf+sizeof(NetPacketHeader), nDataSize); ++nCount; } } std::cout << "已经和服务器断开连接!" << std::endl; } bool TCPClient::OnNetMessage( const unsigned short& nOpcode, const char* pDataBuffer, unsigned short nDataSize ) { switch (nOpcode) { case NET_TEST1: { NetPacket_Test1* pMsg = (NetPacket_Test1*) pDataBuffer; return OnNetPacket(pMsg); } break; default: { std::cout << "收取到未知网络数据包:" << nOpcode << std::endl; return false; } break; } } bool TCPClient::OnNetPacket( NetPacket_Test1* pMsg ) { std::cout << "索引:" << pMsg->nIndex << " 字符串:" << pMsg->arrMessage <<"name:"<<pMsg->name<<"sex:"<<pMsg->sex<<"age:"<<pMsg->age<< std::endl; return true; }
#include "stdafx.h" int _tmain(int argc, _TCHAR* argv[]) { TCPClient client; client.run(); system("pause"); return 0; }
相关文章推荐
- TCP/IP 网络数据封包和解包
- TCP/IP 网络数据封包和解包
- TCP/IP的网络层相关封包与数据
- 鸟哥的 Linux 私房菜笔记 TCP/IP 的网络层相关封包与数据
- 基础网络概念(五)TCP/IP传输层相关封包与数据、TCP三次握手
- 基础网络概念(五)TCP/IP传输层相关封包与数据、TCP三次握手
- NetFlow是一种数据交换方式,提供网络流量的会话级视图,记录下每个TCP/IP事务的信息
- TCP/IP 网络数据传输之网络协议
- 最简单的TCP网络封包解包(补充)-序列化
- 从数据长征和网络协议对TCP/IP,http,socket 进行概念梳理
- 在 TCP 网络上检测出有重复名称,发送消息的计算机的 IP 地址在数据中
- TCP/IP系列之网络分层和数据是怎么从一台主机传送到另外一台主机的
- 最简单的TCP网络封包解包(补充)-序列化
- OSI7层封包解包动态图-数据在网络中的传输过程.gif
- 最简单的TCP网络封包解包(补充)-序列化
- 命名数据网络(NDN)与TCP/IP网络
- 网络层协议IP/ICMP/DHCP/ARP/TCP数据的捕获与分析
- 通过TCPIP网络传输数据经过的三层buffer
- Tcp/Ip网络要点(一)本地数据流转
- Netstat用于显示与IP、TCP、UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况