blueyi's notes

Follow Excellence,Success will chase you!

0%

使用winsock2的sockets进行文件发送

了解socket编程过程,根据MSDN上的Windows Sockets 2文档实现通过socket来发送和接受文件。MSDN上的具体说明:https://msdn.microsoft.com/en-us/library/windows/desktop/ms740673%28v=vs.85%29.aspx

对于C++实现的服务端和客户端过程大致如下:
服务端:

  • 初始化Winsock
  • 创建一个socket作为侦听服务
  • 绑定socket到系统
  • 侦听端口等待客户端连接
  • 接受客户端连接
  • 接收和发送数据
  • 断开连接

客户端:

  • 初始化Winsock
  • 创建用于连接到服务端的socket对象
  • 连接到服务端
  • 发送数据后关闭socket连接,释放资源
  • 将该socket用于接收数据
  • 断开连接

具体代码如下:

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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
/***************服务端*********************/
#undef UNICODE
#define WIN32_LEAN_AND_MEAN

#include <Windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <string>

//需要连接的库
#pragma comment (lib, "Ws2_32.lib")

//定义默认接受数据缓冲区大小及侦听的端口
#define DEFAULT_BUFFLEN 1024
#define DEFAULT_PORT "27015"

int __cdecl main(void)
{
//创建WSADATA对象,WSADATA中包含windows sockets 的实现信息
WSADATA wsaData;
//接收相应函数的返回值
int iResult;
//声明侦听socket和连接服务端的socket
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;

//声明存放ip地址信息的结构体
struct addrinfo *result = NULL;
struct addrinfo hints;

//存放接收到的内容
char recvFileName[DEFAULT_BUFFLEN];
char recvFile[DEFAULT_BUFFLEN];
std::string fileName;

//初始化WINSOCK
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
std::cout << "WSAStartup failed with error:" << iResult << std::endl;
return 1;
}

//分配IP地址存放的结构体空间
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; //指定使用IPV4网址地址
hints.ai_socktype = SOCK_STREAM; //指定socket流
hints.ai_protocol = IPPROTO_TCP; //指定使用TCP协议
hints.ai_flags = AI_PASSIVE; //指定一个标志位

//获取本地IP地址信息
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
std::cout << "WSAStartup failed with error:" << iResult << std::endl;
WSACleanup();
return 1;
}

//创建一个socket用于侦听服务器
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
std::cout << "socket failed with error:" << WSAGetLastError() << std::endl;
freeaddrinfo(result);
WSACleanup();
return 1;
}

//将侦听socket绑定到系统以用于侦听服务器
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
std::cout << "bind failed with error:" << WSAGetLastError() << std::endl;
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
return 1;
}

freeaddrinfo(result);

//侦听端口
iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
std::cout << "listen failed with error:" << WSAGetLastError() << std::endl;
closesocket(ListenSocket);
WSACleanup();
return 1;
}

while (true)
{
std::cout << std::endl;
std::cout << "等待客户端连接..." << std::endl;
//接收客户端的连接,即让客户端socket与侦听socket建立连接
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
std::cout << "accept failed with error:" << WSAGetLastError() << std::endl;
closesocket(ListenSocket);
WSACleanup();
return 1;
}

//接收文件名
std::ofstream out;
iResult = recv(ClientSocket, recvFileName, DEFAULT_BUFFLEN, 0);
if (iResult > 0) {
recvFileName[iResult] = '\0';
fileName = recvFileName;
std::cout << "接收文件名为: " << fileName << std::endl;
out.open(fileName, std::ofstream::out);
}
else {
std::cout << "Receive file name error!" << std::endl;
}

int count = 0;
//接收文件内容直到连接关闭
do {
iResult = recv(ClientSocket, recvFile, DEFAULT_BUFFLEN, 0);
if (iResult == DEFAULT_BUFFLEN) {
for (int i = 0; i < DEFAULT_BUFFLEN; ++i) {
out.put(recvFile[i]);
count++;
}
}
else if (iResult > 0)
{
for (int i = 0; i < iResult; ++i) {
out.put(recvFile[i]);
count++;
}
}
else if (iResult == 0)
std::cout << "接收文件完成" << std::endl;
else {
std::cout << "recv failed with error:" << WSAGetLastError() << std::endl;
closesocket(ClientSocket);
WSACleanup();
return 1;
}
} while (iResult > 0);

out.close();
std::cout << "接收文件大小: " << count << std::endl;

//接收完成后关闭连接
iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
std::cout << "shutdown failed with error:" << WSAGetLastError() << std::endl;
closesocket(ClientSocket);
WSACleanup();
return 1;
}

}
//清空连接并释放资源
closesocket(ClientSocket);
WSACleanup();
return 0;
}

/***************客户端*********************/
//接受两个输入参数,分别表示远程IP和传输文件名,不输入任何参数时,默认使用”127.0.0.1”和”a.txt”

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <iostream>
#include <string>
#include <fstream>

//需要连接的库
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Mswsock.lib")
#pragma comment(lib, "AdvApi32.lib")

//远程端口
#define DEFAULT_PORT "27015"
//默认缓冲区大小
#define DEFAULT_BUFLEN 1024
int __cdecl main(int argc, char **argv)
{
//创建WSADATA对象,WSADATA中包含windows sockets 的实现信息
WSADATA wsaData;
// 声明连接到服务端的socket对象
SOCKET ConnectSocket = INVALID_SOCKET;
//声明addrinfo对象,其中包括sockaddr结构体值
//result和ptr将指向相应的主机地址信息,hints存放socket属性信息
struct addrinfo *result = NULL, *ptr = NULL, hints;
//向服务器发送的数据内容
char sendBuff[DEFAULT_BUFLEN];
std::string fileName = "a.txt";
//设置接收数据的缓冲区大小
int recvbuflen = DEFAULT_BUFLEN;
char recvbuf[DEFAULT_BUFLEN];
int iResult;
std::string ip;
//确定参数是否有效
if (argc == 2) {
ip = argv[1];
std::cout << "远程服务器IP: " << ip << std::endl;
}
else if (argc == 3) {
ip = argv[1];
std::cout << "远程服务器IP: " << ip << std::endl;
fileName = argv[2];
}
else {
ip = "127.0.0.1";
std::cout << "远程服务器IP: " << ip << std::endl;
}
// 初始化winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0)
{
std::cout << "WSAStartup failed: " << iResult << std::endl;
return 1;
}
//为hints分配空间
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
//指定服务器地址和端口,argv[1]为IPV4或者IPV6地址
iResult = getaddrinfo(ip.c_str(), DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
std::cout << "getaddrinfo failed: " << iResult << std::endl;
WSACleanup();
return 1;
}
//尝试连接到服务器直到连接成功
for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
//初始化连接到服务器的socket对象
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
//创建失败则释放资源
if (ConnectSocket == INVALID_SOCKET) {
std::cout << "Error at socket(): " << WSAGetLastError() << std::endl;
freeaddrinfo(result);
WSACleanup();
return 1;
}
//连接到服务器
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
continue;
}
break;
}
//连接完成后释放地址信息占用的内存
freeaddrinfo(result);
//如果连接失败则返回
if (ConnectSocket == INVALID_SOCKET) {
std::cout << "Unable to connect to server!" << std::endl;
WSACleanup();
return 1;
}
//发送文件名
iResult = send(ConnectSocket, fileName.c_str(), (int)fileName.length(), 0);
//发送失败则关闭连接
if (iResult == SOCKET_ERROR) {
std::cout << "send failed: " << WSAGetLastError() << std::endl;
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
std::cout << "文件名所占字节数: " << iResult << std::endl;
std::cout << "发送文件名: " << fileName << std::endl;

//发送文件内容
std::ifstream in(fileName, std::ifstream::in);
int i = 0;
while (in.get(sendBuff[i % DEFAULT_BUFLEN])) {
i++;
if (i % DEFAULT_BUFLEN == 0) {
iResult = send(ConnectSocket, sendBuff, sizeof(sendBuff), 0);
//发送失败则关闭连接
if (iResult == SOCKET_ERROR) {
std::cout << "send failed: " << WSAGetLastError() << std::endl;
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
}
}
iResult = send(ConnectSocket, sendBuff, i % DEFAULT_BUFLEN, 0);
//发送失败则关闭连接
if (iResult == SOCKET_ERROR) {
std::cout << "send failed: " << WSAGetLastError() << std::endl;
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
std::cout << "发送文件大小: " << i << std::endl;
in.close();

//当没有数据需要发送时,首先关闭当前用于发送数据的socket,以便服务端释放资源,并将该socket用于接收数据
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
std::cout << "shutdown failed: " << WSAGetLastError() << std::endl;
closesocket(ConnectSocket);
WSACleanup();
return 1;
}

//从服务端接收数据直到服务器关闭连接
do {
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
if (iResult > 0) {
std::cout << "Bytes received: " << iResult << std::endl;
recvbuf[iResult] = '\0';
std::cout << "Bytes received: " << recvbuf << std::endl;
}
else if (iResult == 0)
std::cout << "断开连接..." << std::endl;
else
std::cout << "recv failed: " << WSAGetLastError() << std::endl;
} while (iResult > 0);

//当接收完数据之后关闭socket并释放连接资源
closesocket(ConnectSocket);
WSACleanup();
return 0;
}

Welcome to my other publishing channels