- Server
#include <WinSock2.h>
#include <windows.h>
#include <iostream>
#define _WINSOCK_DEPRECATED_NO_WARNINGS
using namespace std;
// #pragma comment(lib,"ws2_32.lib") 或者 链接器》》输入》》添加附加依赖项
int main()
{
WORD version = MAKEWORD(2, 2);
WSADATA data;
WSAStartup(version, &data);
// 创建套接字
SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// 绑定监听的网络端口
sockaddr_in _sin = {};
_sin.sin_family = AF_INET;
_sin.sin_port = htons(4567);
_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
int ret = bind(_sock, (sockaddr *)&_sin, sizeof(sockaddr_in));
if (SOCKET_ERROR == ret)
{
cout << "绑定端口失败" << endl;
return -1;
}
cout << "绑定端口成功" << endl;
if (SOCKET_ERROR == listen(_sock, 5))
{
cout << "监听失败" << endl;
return -1;
}
cout << "监听成功" << endl;
// 等待接收客户端的连接
sockaddr_in client_addr = {};
int nRecvLen = sizeof(sockaddr_in);
SOCKET _client = INVALID_SOCKET;
char buf[] = "hello,client";
cout << strlen(buf) << endl;
while (true)
{
_client = accept(_sock, (sockaddr *)&client_addr, &nRecvLen);
if (_client == INVALID_SOCKET)
{
cout << "无效SOCKET" << endl;
}
// 获取客户端的地址
cout << "新客户端加入,IP:" << inet_ntoa(client_addr.sin_addr) << endl;
send(_client, buf, strlen(buf) + 1, 0); //带上结束符号
}
// 关闭socket
closesocket(_sock);
WSACleanup();
return 0;
}
- client
#include <WinSock2.h>
#include <windows.h>
#include <iostream>
//#define _WINSOCK_DEPRECATED_NO_WARNINGS
using namespace std;
// #pragma comment(lib,"ws2_32.lib") 或者 链接器》》输入》》添加附加依赖项
int main()
{
WORD version = MAKEWORD(2, 2);
WSADATA data;
WSAStartup(version, &data);
SOCKET _sock = socket(AF_INET,SOCK_STREAM,0);
if (SOCKET_ERROR == _sock)
{
cout << "绑定端口成功" << endl;
return -1;
}
sockaddr_in _sin = {}; // 需要连接的服务器地址
_sin.sin_family = AF_INET;
_sin.sin_port = htons(4567);
_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
if (SOCKET_ERROR == connect(_sock, (sockaddr *)&_sin, sizeof(sockaddr_in)))
{
cout << "连接失败" << endl;
return -1;
}
cout << "连接服务器成功" << endl;
while (true)
{
char cmdBuf[1024] = {};
scanf("%s", cmdBuf);
if (0 == strcmp(cmdBuf, "exit"))
{
break;
}
else
{
send(_sock, cmdBuf, strlen(cmdBuf)+1 ,0);
}
char recv_buf[1024] = {};
int nLen = recv(_sock, recv_buf, 1024, 0);
cout << "nLen = " << nLen << endl;
if (nLen > 0)
{
cout << "接收到数据:" << recv_buf << endl;
}
}
// 关闭socket
closesocket(_sock);
WSACleanup();
return 0;
}
结构体强转二进制流
struct Data{
int code;
char name[256];
}
Data data = {1,"wang"};
send(socket,(const char *)&data,strlen(data)+1,0);
当存在多个业务时,服务端接收到数据,将无法知道将二进制流强转成哪种结构体对象。在网络通信中,一个报文通常是包含包头和包体的,我们可以构造出来一个结构体,它的包头部分对包体进行描述。
eg:
emun CMD
{
CMD_LOGIN,
CMD_LOGIN_RESULT,
CMD_LOGOUT,
CMD_LOGOUT_RESULT,
ERROR
}
struct DataHeader
{
int dataLength;
int cmd;
}
struct Login : public DataHeader
{
Login()
{
dataLength = sizeof(Login);
cmd = CMD_LOGIN;
}
char username[32];
char password[32];
}
server端recv的时候,分两次读取。第一次读请求头,第二次头部信息读取指定长度请求体。对上面的代码进行简单改造:
- server
#include <WinSock2.h>
#include <windows.h>
#include <iostream>
#define _WINSOCK_DEPRECATED_NO_WARNINGS
using namespace std;
// #pragma comment(lib,"ws2_32.lib") 或者 链接器》》输入》》添加附加依赖项
enum CMD
{
CMD_LOGIN,
CMD_LOGIN_RESULT,
CMD_LOGOUT,
CMD_LOGOUT_RESULT,
CMD_ERROR
};
struct DataHeader
{
int dataLength;
int cmd;
};
struct Login : public DataHeader
{
Login()
{
dataLength = sizeof(Login);
cmd = CMD_LOGIN;
}
char username[32];
char password[32];
};
struct LoginResult : public DataHeader
{
LoginResult()
{
dataLength = sizeof(LoginResult);
cmd = CMD_LOGIN_RESULT;
result = 0;
}
int result;
};
struct Logout : public DataHeader
{
Logout()
{
dataLength = sizeof(Logout);
cmd = CMD_LOGOUT;
}
char username[32];
};
struct LogoutResult : public DataHeader
{
LogoutResult()
{
dataLength = sizeof(LogoutResult);
cmd = CMD_LOGOUT;
result = 0;
}
int result;
};
int main()
{
WORD version = MAKEWORD(2, 2);
WSADATA data;
WSAStartup(version, &data);
// 创建套接字
SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// 绑定监听的网络端口
sockaddr_in _sin = {};
_sin.sin_family = AF_INET;
_sin.sin_port = htons(4567);
_sin.sin_addr.S_un.S_addr = ADDR_ANY;
if (SOCKET_ERROR == bind(_sock, (sockaddr *)&_sin, sizeof(sockaddr_in)))
{
cout << "绑定端口失败" << endl;
return -1;
}
cout << "绑定端口成功" << endl;
if (SOCKET_ERROR == listen(_sock, 5))
{
cout << "监听失败" << endl;
return -1;
}
cout << "监听成功" << endl;
// 等待接收客户端的连接
sockaddr_in client_addr = {};
int nRecvLen = sizeof(sockaddr_in);
cout << "nRecvLen = " << nRecvLen << endl;
SOCKET _client = -1;
char recvBuf[1024] = {};
_client = accept(_sock, (sockaddr *)&client_addr, &nRecvLen);
if (_client == INVALID_SOCKET)
{
cout << "无效SOCKET" << endl;
}
// 获取客户端的地址
cout << "新客户端加入,IP:" << inet_ntoa(client_addr.sin_addr) << endl;
while (true)
{
// 用一个buf先接收请求头部的数据
char szRecv[1024] = {};
int nLen = recv(_client, (char*)&szRecv, sizeof(DataHeader),0);
DataHeader* header = (DataHeader*)szRecv;
if (nLen <= 0)
{
cout << "客户端已经退出" << endl;
break;
}
// cout << "收到命令" << recvBuf << endl;
switch (header->cmd)
{
case CMD_LOGIN:
{
recv(_client, szRecv +sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
Login* login = (Login*)szRecv;
cout << "收到数据:CMD_LOGIN,长度:" << login->dataLength << ",username:" << login->username << ",password" << login->password << endl;
LoginResult loginRes = {};
send(_client,(const char*)&loginRes,sizeof(loginRes),0);
break;
}
case CMD_LOGOUT:
{
recv(_client, szRecv +sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
Logout* logout = (Logout*)szRecv;
cout << "收到数据:CMD_LOGOUT,长度:" << logout->dataLength << ",username:" << logout->username << endl;
LogoutResult logoutRes = {};
send(_client, (const char*)&logoutRes, sizeof(logoutRes), 0);
break;
}
default:
DataHeader header = { 0,CMD_ERROR };
/*header->cmd = CMD_ERROR;
header->dataLength = 0;*/
send(_client, (const char*)&header, sizeof(header), 0);
break;
}
}
// 关闭socket
closesocket(_sock);
WSACleanup();
return 0;
}
- client
#include <WinSock2.h>
#include <windows.h>
#include <iostream>
//#define _WINSOCK_DEPRECATED_NO_WARNINGS
using namespace std;
// #pragma comment(lib,"ws2_32.lib") 或者 链接器》》输入》》添加附加依赖项
enum CMD
{
CMD_LOGIN,
CMD_LOGIN_RESULT,
CMD_LOGOUT,
CMD_LOGOUT_RESULT,
CMD_ERROR
};
struct DataHeader
{
int dataLength;
int cmd;
};
struct Login : public DataHeader
{
Login()
{
dataLength = sizeof(Login);
cmd = CMD_LOGIN;
}
char username[32];
char password[32];
};
struct LoginResult : public DataHeader
{
LoginResult()
{
dataLength = sizeof(LoginResult);
cmd = CMD_LOGIN_RESULT;
result = 0;
}
int result;
};
struct Logout : public DataHeader
{
Logout()
{
dataLength = sizeof(Logout);
cmd = CMD_LOGOUT;
}
char username[32];
};
struct LogoutResult : public DataHeader
{
LogoutResult()
{
dataLength = sizeof(LogoutResult);
cmd = CMD_LOGOUT;
result = 0;
}
int result;
};
int main()
{
WORD version = MAKEWORD(2, 2);
WSADATA data;
WSAStartup(version, &data);
SOCKET _sock = socket(AF_INET,SOCK_STREAM,0);
if (SOCKET_ERROR == _sock)
{
cout << "绑定端口成功" << endl;
return -1;
}
sockaddr_in _sin = {}; // 需要连接的服务器地址
_sin.sin_family = AF_INET;
_sin.sin_port = htons(4567);
_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
if (SOCKET_ERROR == connect(_sock, (sockaddr *)&_sin, sizeof(sockaddr_in)))
{
cout << "连接失败" << endl;
return -1;
}
cout << "连接服务器成功" << endl;
while (true)
{
char cmdBuf[1024] = {};
scanf("%s", cmdBuf);
if (0 == strcmp(cmdBuf, "login"))
{
Login login;
strcpy(login.username,"scott");
strcpy(login.password,"123456");
send(_sock, (const char*)&login, sizeof(login), 0);
// 接收返回值
LoginResult loginRes = {};
recv(_sock,(char*)&loginRes,sizeof(loginRes),0);
cout << "返回值:" << loginRes.result << endl;
}
else if (0 == strcmp(cmdBuf, "logout"))
{
Logout logout = {};
strcpy(logout.username, "scott");
send(_sock, (const char*)&logout, sizeof(logout), 0);
// 接收返回值
LogoutResult logoutRes = {};
recv(_sock, (char*)&logoutRes, sizeof(logoutRes), 0);
cout << "返回值:" << logoutRes.result << endl;
}
else if (0 == strcmp(cmdBuf, "exit"))
{
cout << "停止连接" << endl;
break;
}
else
{
cout << "输入错误指令" << endl;
break;
}
}
// 关闭socket
closesocket(_sock);
WSACleanup();
return 0;
}
base版本的就是这些,但是是阻塞式单收发的,这在实际的开发中肯定不行,我们需要一种不用一直阻塞的方法来收发消息。