【c++项目】从零开始写一个局域网聊天室02:结构体类型数据传输

本文最后更新于:2022年7月9日 下午

【c++项目】从零开始写一个局域网聊天室02:结构体类型数据传输

详细的过程我都以注释的形式写在代码里,这里就不啰嗦了。

server端

server.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/*
2022-6-22
author:连思鑫
结构体类型数据传输server端
*/

#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <windows.h>
#include <WinSock2.h>
#include "iostream"
#include "stdio.h"
using namespace std;
/*使用报文的方式进行传输*/
//数据头
enum CMD
{
CMD_LOGIN,
CMD_LOGINOUT,
CMD_ERROR
};
struct DataHeader
{
short dataLength;//数据长度
short cmd;//命令
};
//DataPackage
//包体
struct Login
{
char userName[32];
char PassWord[32];
};
struct LoginResult
{
char result[32];
};
struct LoginOut
{
char userName[32];
};
struct LoginOutResult
{
char result[32];
};

int main()
{
/*启动Windows socket 2.x环境*/
//版本号
WORD ver = MAKEWORD(2, 2);
WSADATA dat;
//socket网络编程启动函数
WSAStartup(ver, &dat);
//---------------------------
//--用Socket API建立简易TCP服务端
//1、建立一个socket 套接字 (windows) linux上指的是指针
/*socket(
_In_ int af,(表示什么类型的套接字)
_In_ int type,(数据流)
_In_ int protocol
);*/
//IPV4的网络套接字 AF_INET
//IPV6的网络套接字 AF_INET6
SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
char msgBuf[] = "Hello, I'm Server.";
//2、bind 绑定用于接收客户端链接的网络端口
/*
bind(
_In_ SOCKET s,
_In_reads_bytes_(namelen) const struct sockaddr FAR * name,
_In_ int namelen
);
*/
sockaddr_in _sin = {};
_sin.sin_family = AF_INET;//ipv4
_sin.sin_port = htons(4567);//端口号 由于主机和网络的数据类型不同 因此需要进行转换 使用 host to net unsigned short
_sin.sin_addr.S_un.S_addr = INADDR_ANY;//inet_addr("127.0.0.1");//服务器的ip地址 INADDR_ANY本机所有的Ip地址都可以访问 一般这样
//有可能绑定失败
if (SOCKET_ERROR == bind(_sock, (sockaddr*)&_sin, sizeof(_sin)))
{
printf("错误,绑定网络端口失败...\n");
}
else
{
printf("绑定端口成功...\n");
}

//3、listen 监听网络端口
/*
listen(
_In_ SOCKET s,
_In_ int backlog
);*/
if (SOCKET_ERROR == listen(_sock, 5))
{
printf("错误,监听网络端口失败...\n");
}
else
{
printf("监听网络端口成功...\n");
}

//4、accept 等待接收客户端链接
/*
accept(
_In_ SOCKET s,
_Out_writes_bytes_opt_(*addrlen) struct sockaddr FAR * addr,
_Inout_opt_ int FAR * addrlen
);*/
sockaddr_in clientAddr = {};//客户端地址
int nAddrLen = sizeof(sockaddr_in);//地址长度
SOCKET _cSocket = INVALID_SOCKET;

_cSocket = accept(_sock, (sockaddr*)&clientAddr, &nAddrLen);
if (INVALID_SOCKET == _cSocket)
{
printf("错误,接收到无效客户端SOCKET...\n");
}
printf("新客户端加入: socket = %d IP = %s \n", _cSocket, inet_ntoa(clientAddr.sin_addr));//inet_ntoa转换为可读地址
//一直不停的收发数据
//char _recvBuf[128] = {};//接收缓冲区
while (true)
{
//使用header来接受客户端传过来的命令数据 通过命令判断客户端的需求
DataHeader header = {};
//5 接收客户端请求数据
int nLen = recv(_cSocket, (char*)&header, sizeof(DataHeader), 0);
if (nLen <= 0)
{
printf("客户端已经退出, 任务结束。");
break;
}
printf("收到命令: %d 数据长度:%d\n", header.cmd, header.dataLength);
switch (header.cmd)
{
case CMD_LOGIN:
{
Login login = {};
recv(_cSocket, (char*)&login, sizeof(login), 0);
//忽略判断用户名密码是否正确的过程
LoginResult ret = { "登录成功!!!"};
send(_cSocket, (char*)&header, sizeof(DataHeader), 0);
send(_cSocket, (char*)&ret, sizeof(LoginResult), 0);
}
break;
case CMD_LOGINOUT:
{
LoginOut loginout = {};
recv(_cSocket, (char*)&loginout, sizeof(LoginOut), 0);
//忽略判断用户名密码是否正确的过程
LoginOutResult ret = { "注销成功!!"};
send(_cSocket, (char*)&header, sizeof(DataHeader), 0);
send(_cSocket, (char*)&ret, sizeof(LoginOutResult), 0);
}
break;
default:
header.cmd = CMD_ERROR;
header.dataLength = 0;
send(_cSocket, (char*)&header, sizeof(DataHeader), 0);
break;
}

}
//5、send 向客户端发送一条数据
/*send(
_In_ SOCKET s,
_In_reads_bytes_(len) const char FAR * buf,
_In_ int len,
_In_ int flags
);*/
//char msgBuf[] = "Hello, I'm Server.";
//+1表示将结尾符一并发送过去
//send(_cSocket, msgBuf, strlen(msgBuf) + 1, 0);

//6、关闭套接字closesocket
closesocket(_sock);

//---------------------------
WSACleanup();
printf("已退出,任务结束");
getchar();
cout << "hello..." << endl;
return 0;
}

client端

client.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
2022-6-22
author:连思鑫
结构体类型数据传输client端
*/

#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <windows.h>
#include <WinSock2.h>
#include <stdio.h>
#include <iostream>
using namespace std;
enum CMD
{
CMD_LOGIN,
CMD_LOGINOUT,
CMD_ERROR
};
struct DataHeader
{
short dataLength;//数据长度
short cmd;//命令
};
//DataPackage
//包体
struct Login
{
char userName[32];
char PassWord[32];
};
struct LoginResult
{
char result[32];
};
struct LoginOut
{
char userName[32];
};
struct LoginOutResult
{
char result[32];
};

int main()
{
/*启动Windows socket 2.x环境*/
//版本号
WORD ver = MAKEWORD(2, 2);
WSADATA dat;
//socket网络编程启动函数
WSAStartup(ver, &dat);
//---------------------------
//用Socket API建立简易TCP客户端
//1、建立一个socket
SOCKET _sock = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == _sock)
{
printf("错误,建立Socket失败...\n");
}
else
{
printf("建立socket成功...\n");
}

//2、链接服务器 connect
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 = connect(_sock, (sockaddr*)&_sin, sizeof(sockaddr_in)); //使用sizeof(sockaddr_in)类型更安全
if (SOCKET_ERROR == ret)
{
printf("错误,链接服务器失败...\n");
}
else
{
printf("链接服务器成功...\n");
}
while (true)
{
//3 输入请求命令
char cmdBuf[128] = {};
scanf("%s", cmdBuf);

//4 处理请求命令
if (0 == strcmp(cmdBuf, "exit"))
{
printf("收到exit命令,任务结束");
break;
}
else if (0 == strcmp(cmdBuf, "login"))
{
Login login = { "lsx", "lsxyyds" };
DataHeader dh = {sizeof(login), CMD_LOGIN};
//login.userName = "lsx";
//login.PassWord = "lsxyyds";
//5 向服务器发送请求命令
send(_sock, (const char*)&dh, sizeof(dh), 0);
send(_sock, (const char*)&login, sizeof(login), 0);
//接收服务器返回的数据
DataHeader retHeader = {};
LoginResult loginRet = {};
recv(_sock, (char*)&retHeader, sizeof(retHeader), 0);
recv(_sock, (char*)&loginRet, sizeof(loginRet), 0);
//printf("LoginResult: %s", loginRet.result);
cout << "LoginResult:" << loginRet.result << endl;
}
else if (0 == strcmp(cmdBuf, "logout"))
{
LoginOut logout = {"lsx"};
DataHeader dh = {sizeof(logout), CMD_LOGINOUT };
//5 向服务器发送请求命令
send(_sock, (const char*)&dh, sizeof(dh), 0);
send(_sock, (const char*)&logout, sizeof(logout), 0);
//接收服务器返回的数据
DataHeader retHeader = {};
LoginOutResult loginoutRet = {};
recv(_sock, (char*)&retHeader, sizeof(retHeader), 0);
recv(_sock, (char*)&loginoutRet, sizeof(loginoutRet), 0);
cout << "LogoutResult:" << loginoutRet.result << endl;
}
else
{
printf("不支持的命令,请重新输入。\n");
}
}

//7 关闭套节字closesocket
closesocket(_sock);

//---------------------------
WSACleanup();
printf("已退出.");
getchar();
return 0;
}

成果展示

upload successful

server.exe体验:点我下载

client.exe体验:点我下载


【c++项目】从零开始写一个局域网聊天室02:结构体类型数据传输
https://jinbilianshao.github.io/2022/06/22/【c-项目】从零开始写一个局域网聊天室02:结构体类型数据传输/
作者
连思鑫
发布于
2022年6月22日
许可协议