When C++ Socket transmits network data, the data is generally a char type character array. In addition, there are some methods to transmit the data types defined by ourselves
- Custom structure
- Json serialization
- Define Class object
1. Structure
Define a structure, for example:
struct DataPack { int age; string name; };
When sending data, process the data and forcibly convert the pointer of DataPack type to the pointer of char type, as follows:
DataPack da = { 20, "anthony" }; send(cSock, (char *)&da, sizeof(da), 0);
When receiving data, define a structure of the same type to parse the string:
DataPack tmp; int nlen = recv(sock, (char *)&tmp, 128, 0); if (nlen > 0) { cout << "Received data:" << tmp.age << " " << tmp.name << endl; }
Alternatively:
int nlen = recv(sock, recvBuf, 128, 0); // recvBuf is the buffer for receiving data if (nlen > 0) { DataPack* da = (DataPack*)recvBuf; // Convert char type pointer to DataPack type pointer cout << "Received data:" << tmp->age << " " << tmp->name << endl; }
1.1 network data message
In order to reduce errors in data transmission and enhance stability, we can use network data message:
Similar to the message format of network protocol, the message has two parts: packet header and packet body, which are regarded as the basic unit of network message
Packet header: describes the size and function of the packet sent this time
Packet header: the data part of the packet
Next, we define a message structure for login and logout:
// The role of messages or commands in the header (login, exit) enum CMD { CMD_LOGIN, CMD_LogOut, CMD_ERROR }; // Baotou struct DataHeader { int lenth; int cmd; }; // Login data struct Login { char UserName[32]; char passWord[32]; }; // Login results struct LoginSucceed { int result; }; // Log out struct LogOut { char UserName[32]; }; // Results of logout struct LogOutSucceed { int result; };
Each time data is sent, the packet header is sent first, and the data is sent in the data sending part
Server implementation:
// After accepting the connection from the client cSock = accept(sock, (sockaddr*)&clientAddr, &nAddrLen); if (INVALID_SOCKET == cSock) { cout << "Error, invalid client received SOCKET..." << endl; } cout << "New client join:" << inet_ntoa(clientAddr.sin_addr) << endl; //5 send sends data to the client while (true) { DataHeader header = {}; int nlen = recv(cSock, (char*)&header, sizeof(header), 0); if (nlen <= 0) { cout << "Client exit..." << endl; break; } cout << "Command received:" << header.cmd << " Data length:" << header.lenth << endl; //Processing requests 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; } }
Client implementation:
if (SOCKET_ERROR == connect(sock, (sockaddr*)&sin, sizeof(sockaddr_in))) { cout << "Failed to establish connection..." << endl; } else { cout << "Connection established successfully..." << 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 request command to server send(sock, (char*)&dh, sizeof(dh), 0); send(sock, (char*)&login, sizeof(login), 0); //Receiving server return data 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 request command to server send(sock, (char*)&dh, sizeof(dh), 0); send(sock, (char*)&logout, sizeof(logout), 0); //Receiving server return data 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 << "Invalid command, please re-enter:" << endl; } }

1.2 change from multiple sending and receiving messages to one sending and receiving
In order to reduce errors, we can encapsulate multiple structures by aggregation or inheritance, as follows:
// The inheritance method is used here. In addition, you can also directly define another structure in one structure // For example: 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; };
Server implementation:
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 << "Client exit..." << endl; break; } //Processing requests switch (login.cmd) { case CMD_LOGIN: { /*Login login; recv(cSock, (char*)&login, sizeof(login), 0);*/ cout << "Command received: CMD_LOGIN" << " Data length:" << 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 << "Command received: CMD_LOGIN" << " Data length:" << 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; } }
client:
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 request command to server send(sock, (char*)&login, sizeof(login), 0); //Receiving server return data 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 request command to server send(sock, (char*)&logout, sizeof(logout), 0); //Receiving server return data LogOutSucceed logoutRet; recv(sock, (char*)&logoutRet, sizeof(logoutRet), 0); cout << "LogOutSucceed: " << logoutRet.result << endl; } else { cout << "Invalid command, please re-enter:" << endl; } }
2. Use Jason (try later)
The full name of JSON is JavaScript Object Notation, which is a lightweight data transmission format. When C + + processes JSON format data, it needs to use a third-party library. The famous one is jsoncpp. The download address is:
3. Define Class object
Similar to structure, when transferring custom object data, you can convert the type of pointer.