赞
踩
C++ Socket在进行网络数据的传送时,数据一般是char类型的字符数组,除此之外还有一些方法可以传送我们自己定义的数据类型
- 自定义结构体
- Json序列化
- 定义Class对象
定义一个结构体,例如:
struct DataPack
{
int age;
string name;
};
在发送数据的时候对数据进行处理,将DataPack类型的指针强制转换成char类型指针,具体如下:
DataPack da = { 20, "anthony" };
send(cSock, (char *)&da, sizeof(da), 0);
接收数据的时候,定义一个相同类型的结构体对这个字符串进行解析:
DataPack tmp;
int nlen = recv(sock, (char *)&tmp, 128, 0);
if (nlen > 0)
{
cout << "接受到数据:" << tmp.age << " " << tmp.name << endl;
}
或者也可以这样:
int nlen = recv(sock, recvBuf, 128, 0); // recvBuf为接收数据的缓冲区
if (nlen > 0)
{
DataPack* da = (DataPack*)recvBuf; // 将char类型指针转换成DataPack类型指针
cout << "接受到数据:" << tmp->age << " " << tmp->name << endl;
}
为了减少数据传送过程中出现的错误以及增强稳定性,我们可以使用网络数据报文的方式:
与网络协议的报文格式类似,报文有两个部分:包头和包体,将其作为网络消息的基本单元
包头:描述本次发送的数据包的大小和作用
包头:包的数据部分
下面我们定义一个登录和退出登录的消息结构:
// 包头中关于消息或命令的作用(登录,退出) enum CMD { CMD_LOGIN, CMD_LogOut, CMD_ERROR }; // 包头 struct DataHeader { int lenth; int cmd; }; // 登录数据 struct Login { char UserName[32]; char passWord[32]; }; // 登录的结果 struct LoginSucceed { int result; }; // 退出登录 struct LogOut { char UserName[32]; }; // 退出登录的结果 struct LogOutSucceed { int result; };
每次发送数据时,先发送包头,在发送数据部分
服务端实现:
// 接受客户端的连接后 cSock = accept(sock, (sockaddr*)&clientAddr, &nAddrLen); if (INVALID_SOCKET == cSock) { cout << "错误,接受到无效客户端SOCKET..." << endl; } cout << "新客户端加入:" << inet_ntoa(clientAddr.sin_addr) << endl; //5 send 向客户端发送数据 while (true) { DataHeader header = {}; int nlen = recv(cSock, (char*)&header, sizeof(header), 0); if (nlen <= 0) { cout << "客户端退出..." << endl; break; } cout << "接收到命令:" << header.cmd << " 数据长度:" << header.lenth << endl; //处理请求 switch (header.cmd) { case CMD_LOGIN: { Login login; int nlen = recv(cSock, (char*)&login, sizeof(login), 0); LoginSucceed ret = { 1 }; send(cSock, (char*)&header, sizeof(header), 0); send(cSock, (char*)&ret, sizeof(ret), 0); } break; case CMD_LogOut: { LogOut logout; recv(cSock, (char*)&logout, sizeof(logout), 0); LogOutSucceed ret = { 1 }; send(cSock, (char*)&header, sizeof(header), 0); send(cSock, (char*)&ret, sizeof(ret), 0); } break; default: header.cmd = CMD_ERROR; header.lenth = 0; send(cSock, (char*)&header, sizeof(header), 0); break; } }
客户端实现:
if (SOCKET_ERROR == connect(sock, (sockaddr*)&sin, sizeof(sockaddr_in))) { cout << "建立连接失败..." << endl; } else { cout << "建立连接成功..." << endl; } while (true) { string comBuf; cin >> comBuf; if (comBuf == "exit") { break; } else if (comBuf == "login") { Login login = { "anthony", "chen" }; DataHeader dh = {sizeof(login), CMD_LOGIN}; //向服务器发送请求命令 send(sock, (char*)&dh, sizeof(dh), 0); send(sock, (char*)&login, sizeof(login), 0); //接收服务器返回数据 DataHeader retHeader; LoginSucceed loginRet; recv(sock, (char*)&retHeader, sizeof(retHeader), 0); recv(sock, (char*)&loginRet, sizeof(loginRet), 0); cout << "LoginSucceed: " << loginRet.result << endl; } else if (comBuf == "logout") { LogOut logout = { "anthony" }; DataHeader dh = { sizeof(logout), CMD_LogOut}; //向服务器发送请求命令 send(sock, (char*)&dh, sizeof(dh), 0); send(sock, (char*)&logout, sizeof(logout), 0); //接收服务器返回数据 DataHeader retHeader; LogOutSucceed logoutRet; recv(sock, (char*)&retHeader, sizeof(retHeader), 0); recv(sock, (char*)&logoutRet, sizeof(logoutRet), 0); cout << "LogOutSucceed: " << logoutRet.result << endl; } else { cout << "命令无效,请重新输入:" << endl; } }
为了减少出错,我们可以将多个结构体进行封装,使用聚合或继承的方式,如下:
// 这里使用继承的方式,除此之外也可以直接在一个结构体定义一个另外的结构体 // 例如:struct Login{DataHeader header; char UserName[32]; char passWord[32];} enum CMD { CMD_LOGIN, CMD_LogOut, CMD_LOGIN_SU, CMD_LOGOUT_SU, CMD_ERROR }; struct DataHeader { int lenth; int cmd; }; // Data Package struct Login : public DataHeader { Login() { lenth = sizeof(Login); cmd = CMD_LOGIN; } char UserName[32]; char passWord[32]; }; struct LoginSucceed : public DataHeader { LoginSucceed() { lenth = sizeof(LoginSucceed); cmd = CMD_LOGIN_SU; } int result; }; struct LogOut : public DataHeader { LogOut() { lenth = sizeof(LogOut); cmd = CMD_LogOut; } char UserName[32]; }; struct LogOutSucceed : public DataHeader { LogOutSucceed() { lenth = sizeof(LogOutSucceed); cmd = CMD_LOGOUT_SU; } int result; };
服务端实现:
while (true) { /*DataHeader header = {}; int nlen = recv(cSock, (char*)&header, sizeof(header), 0);*/ Login login; int nlen = recv(cSock, (char*)&login, sizeof(login), 0); if (nlen <= 0) { cout << "客户端退出..." << endl; break; } //处理请求 switch (login.cmd) { case CMD_LOGIN: { /*Login login; recv(cSock, (char*)&login, sizeof(login), 0);*/ cout << "接收到命令:CMD_LOGIN" << " 数据长度:" << login.lenth << " UserName: " << login.UserName << " PassWord: " << login.passWord << endl; LoginSucceed ret; send(cSock, (char*)&ret, sizeof(ret), 0); } break; case CMD_LogOut: { /*LogOut logout; recv(cSock, (char*)&logout, sizeof(logout), 0);*/ cout << "接收到命令:CMD_LOGIN" << " 数据长度:" << login.lenth << " UserName: " << login.UserName << endl; LogOutSucceed ret; send(cSock, (char*)&ret, sizeof(ret), 0); } break; default: login.cmd = CMD_ERROR; login.lenth = 0; send(cSock, (char*)&login, sizeof(login), 0); break; } }
客户端:
while (true) { string comBuf; cin >> comBuf; if (comBuf == "exit") { break; } else if (comBuf == "login") { Login login; strcpy(login.UserName, "anthony"); strcpy(login.passWord, "chen"); //向服务器发送请求命令 send(sock, (char*)&login, sizeof(login), 0); //接收服务器返回数据 LoginSucceed loginRet; recv(sock, (char*)&loginRet, sizeof(loginRet), 0); cout << "LoginSucceed: " << loginRet.result << endl; } else if (comBuf == "logout") { LogOut logout; strcpy(logout.UserName, "anthony"); //向服务器发送请求命令 send(sock, (char*)&logout, sizeof(logout), 0); //接收服务器返回数据 LogOutSucceed logoutRet; recv(sock, (char*)&logoutRet, sizeof(logoutRet), 0); cout << "LogOutSucceed: " << logoutRet.result << endl; } else { cout << "命令无效,请重新输入:" << endl; } }
JSON 的全称为:JavaScript Object Notation,是一种轻量级的数据传输格式。C++处理JSON格式数据时,需要使用到第三方库,比较出名的是jsoncpp,下载地址为:json-cpp下载
和结构体类似,在传输自定义的对象数据时,转换指针的类型即可。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。