TCP/IP 网络数据封包和解包
来源:互联网 发布:抓取数据 编辑:程序博客网 时间:2024/04/30 23:58
TCP/IP 网络数据以流的方式传输,数据流是由包组成,如何判定接收方收到的包是否是一个完整的包就要在发送时对包进行处理,这就是封包技术,将包处理成包头,包体。包头是包的开始标记,整个包的大小就是包的结束标记。接收方只要按同样的方式解包即可,下面是我在网上搜罗的一个网络服务端和客户端程序代码,整理了下。
服务器代码
common.h
#pragma once #define NET_PACKET_DATA_SIZE 1024 #define NET_PACKET_SIZE (sizeof(NetPacketHeader) + NET_PACKET_DATA_SIZE) * 10 #define SERVER_PORT 6000/// 网络数据包包头 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];};
TCPServer.h
#pragma once #include <iostream>#include <stdlib.h>#include <winsock2.h>#include "common.h"using namespace std;#pragma comment(lib, "ws2_32.lib")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];};
TCPServer.cpp
#include "TCPServer.h"TCPServer::TCPServer() : mServerSocket(INVALID_SOCKET){ WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD(1, 1); err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { return ; } if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) { WSACleanup(); return ; } // 创建套接字 mServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if (mServerSocket == INVALID_SOCKET) { cout << "创建套接字失败!" << endl; return; } // 填充服务器的IP和端口号 mServerAddr.sin_family = AF_INET; mServerAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); mServerAddr.sin_port = htons(SERVER_PORT); // 绑定IP和端口 if (::bind(mServerSocket, (sockaddr*)&mServerAddr, sizeof(mServerAddr)) == SOCKET_ERROR) { cout << "绑定IP和端口失败!" << endl; return; } // 监听客户端请求,最大同时连接数设置为10. if (::listen(mServerSocket, SOMAXCONN) == SOCKET_ERROR) { cout << "监听端口失败!" << endl; return; } cout << "启动TCP服务器成功!" << endl;}TCPServer::~TCPServer(){ ::closesocket(mServerSocket); cout << "关闭TCP服务器成功!" << endl;}void TCPServer::run(){ // 接收客户端的连接 acceptClient(); int nCount = 0; for (;;) { if (mAcceptSocket == INVALID_SOCKET) { cout << "客户端主动断开了连接!" << endl; break; } // 发送数据包 NetPacket_Test1 msg;//消息类型 msg.nIndex = nCount; msg.age = 23; strcpy_s(msg.arrMessage, "北京市朝阳区"); strcpy_s(msg.name, "天策"); strcpy_s(msg.sex, "男"); //strncpy(msg.name, "天策", sizeof(msg.name)); //strncpy(msg.sex, "男", sizeof(msg.sex)); bool bRet = SendData(NET_TEST1, (const char*)&msg, sizeof(msg));//强制类型转换为字符串类型 if (bRet) { cout << "发送数据成功!" << endl; } else { cout << "发送数据失败!" << endl; break; } ++nCount; }}void TCPServer::closeClient(){ // 判断套接字是否有效 if (mAcceptSocket == INVALID_SOCKET) return; // 关闭客户端套接字 ::closesocket(mAcceptSocket); cout << "客户端套接字已关闭!" << endl;}void TCPServer::acceptClient(){ // 以阻塞方式,等待接收客户端连接 int nAcceptAddrLen = sizeof(mAcceptAddr); mAcceptSocket = ::accept(mServerSocket, (struct sockaddr*)&mAcceptAddr, &nAcceptAddrLen); cout << "接受客户端IP:" << inet_ntoa(mAcceptAddr.sin_addr) << endl;}bool TCPServer::SendData(unsigned short nOpcode, const char* pDataBuffer, const unsigned int& nDataSize){ //pHead指向发送缓冲数组 NetPacketHeader* pHead = (NetPacketHeader*)m_cbSendBuf;// pHead->wOpcode = nOpcode;//操作码 // 数据封包 if ((nDataSize > 0) && (pDataBuffer != 0)) { //把将要发送的数据放到pHead指向的下一个位置(发送缓冲数组中) 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;}
myMain.cpp
#include "TCPServer.h"int main(){ TCPServer server; server.run(); system("pause"); return 0;}
客户端代码
TCPClient.h
#pragma once#include <iostream>#include <stdlib.h>#include <winsock2.h>#include "common.h"using namespace std;#pragma comment(lib, "ws2_32.lib")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 mClientSocket; // 服务器地址 sockaddr_in mServerAddr; char m_cbRecvBuf[NET_PACKET_SIZE]; char m_cbDataBuf[NET_PACKET_SIZE]; int m_nRecvSize;};
TCPClient.cpp
#include "TCPClient.h"TCPClient::TCPClient(){ WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD(1, 1); err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { return ; } if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) { WSACleanup(); return ; } memset(m_cbRecvBuf, 0, sizeof(m_cbRecvBuf)); m_nRecvSize = 0; // 创建套接字 mClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if (mClientSocket == INVALID_SOCKET) { cout << "创建套接字失败!" << endl; return; } // 填充服务器的IP和端口号 mServerAddr.sin_family = AF_INET; mServerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); mServerAddr.sin_port = htons(SERVER_PORT);//(u_short) // 连接到服务器 if (::connect(mClientSocket, (struct sockaddr*)&mServerAddr, sizeof(mServerAddr))) { ::closesocket(mClientSocket); cout << "连接服务器失败!" << endl; return; } cout << "连接服务器成功!" << endl;}TCPClient::~TCPClient(){ ::closesocket(mClientSocket);}void TCPClient::run(){ int nCount = 0; for (;;) { // 接收数据 int nRecvSize = ::recv(mClientSocket ,m_cbRecvBuf + m_nRecvSize, sizeof(m_cbRecvBuf) - m_nRecvSize, 0); //一个完整的包接收过程中,m_nRecvSize 不断增加,保证接收的数据不超过 m_cbRecvBuf 的大小 if (nRecvSize <= 0) { cout << "服务器主动断开连接!" << endl; break; } // 保存已经接收数据的大小 m_nRecvSize += nRecvSize; // 接收到的数据够不够一个包头的长度 //已经收到一个完整的包,如果没用收到一个完整的包,此处循环不执行,继续下一轮循环 while (m_nRecvSize >= sizeof(NetPacketHeader)) { // 收够5个包,主动与服务器断开 if (nCount >= 5) { cout << "收够5个包,主动与服务器断开!" << endl; ::closesocket(mClientSocket); break; } // 读取包头 NetPacketHeader* pHead = (NetPacketHeader*)(m_cbRecvBuf); const unsigned short nPacketSize = pHead->wDataSize; // 判断是否已接收到足够一个完整包的数据 除包头外???? //if((m_nRecvSize - sizeof(NetPacketHeader)) < nPacketSize) if (m_nRecvSize < nPacketSize) { // 还不够拼凑出一个完整包 cout << "没有接收到完整的数据!" << endl; break; } // 拷贝到数据缓存 CopyMemory(m_cbDataBuf, m_cbRecvBuf, nPacketSize);//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)//m_cbDataBuf本身包括包头,为什么加上包头大小?? , nDataSize); ++nCount; } } cout << "已经和服务器断开连接!" << 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: { cout << "收取到未知网络数据包:" << nOpcode << endl; return false; } break; }}bool TCPClient::OnNetPacket(NetPacket_Test1* pMsg){ cout << "索引:" << pMsg->nIndex << " 字符串:" << pMsg->arrMessage << "name:" << pMsg->name << "sex:" << pMsg->sex << "age:" << pMsg->age << endl; return true;}
myMain.cpp
#include "TCPClient.h"int main(){ TCPClient client; client.run(); system("pause"); return 0;}
0 0
- TCP/IP 网络数据封包和解包
- TCP/IP 网络数据封包和解包
- TCP/IP 网络数据封包和解包
- socket 封包和解包
- socket 封包和解包
- socket 封包和解包
- 基础网络概念(五)TCP/IP传输层相关封包与数据、TCP三次握手
- 基础网络概念(五)TCP/IP传输层相关封包与数据、TCP三次握手
- 鸟哥的 Linux 私房菜笔记 TCP/IP 的网络层相关封包与数据
- 网络之 TCP封包、粘包、半包
- H.264的RTP封包和解包
- H.264的RTP封包和解包
- TCP网络协议编程的封包和拆包
- tcp封包才,解包
- Java中的自动封包和解包(Autoboxing和AutoUnboxing)
- 关于TCP封包、粘包、半包
- 关于TCP封包、粘包、半包
- 关于TCP封包、粘包、半包
- java发送邮件
- Dapper的基本使用
- Matlab界面中选项卡的切换
- 第一次在这里安家啦。感觉萌萌哒
- 2016.1.5 CSDN多少积分才能显示排名呢?今天终于不是千里之外了~~哈哈哈
- TCP/IP 网络数据封包和解包
- 改变progressBar滑块颜色和进度条颜色
- Java设计模式_结构型_外观模式_病人去医院看病
- Request failed: unacceptable content-type: text/html" AFNetworking - 芒果iOS开发
- Python 格式化字符串的三种方法
- Maven项目部署到服务器如何设置访问路径-配置虚拟目录
- php windows下的定时程序
- [Android实例] 仿微信摇一摇功能实现
- Android getDecorView用途——屏幕截图