C/C++ Socket编程

上个月的计算机网络课程设计结课了,整理一下。 共四个题:
  1. 实现简单的服务器-客户端通信。
  2. 实现1.的多线程版。
  3. 用RawSocket监听网络。
  4. 实现文件传输。
进入正题之前先插一个小题目:
『智力题』AB各有一把钥匙和锁,现在A要用加锁的盒子通过快递C传一个东西给B,但是如果C能打开盒子的话就会私吞这个东西,问AB该怎么做才能确保东西能从A传给B。(面试题)
文章最后可能有答案。
第一题纯属上手,熟悉一般流程。 服务器端
  1. 引入头文件
  2. 设置编译链接选项
  3. 加载套接字库
  4. 创建套接字
  5. 初始化地址族变量
  6. 绑定到地址和端口
  7. 监听
  8. 等待连接
  9. 发送接收
客户端第6步开始是
  1. 连接服务器
  2. 发送接收

点击显示/隐藏代码

  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
#include<iostream>
#include<WinSock2.h>
#include<ctime>
#pragma comment(lib, "ws2_32.lib")
//#pragma comment(lib, "Ws2_32.lib")
#define CONNECT_NUM_MAX 10
#define _WINSOCK_DEPRECATED_NO_WARNINGS
using namespace std;

//http://www.js-code.com/C_yuyan/20150918/4488.html
int main(int argc, char** argv)
{
	int port = 1234;
	if (argc > 1)
	{
		port = atoi(argv[1]);
		if (port < 1024 || port>65534)
		{
			printf("Port error. Use %s port\n", argv[0]);
		}
	}
	//加载套接字库
	WSADATA wsaData;
	int iRet = 0;
	iRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iRet != 0)
	{
		cout << "WSAStartup(MAKEWORD(2, 2), &wsaData) execute failed!" << endl;;
		return -1;
	}
	if (2 != LOBYTE(wsaData.wVersion) || 2 != HIBYTE(wsaData.wVersion))
	{
		WSACleanup();
		cout << "WSADATA version is not correct!" << endl;
		return -1;
	}

	//创建套接字
	SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);
	if (serverSocket == INVALID_SOCKET)
	{
		cout << "serverSocket = socket(AF_INET, SOCK_STREAM, 0) execute failed!" << endl;
		return -1;
	}

	//初始化服务器地址族变量
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(port);

	//绑定
	iRet = bind(serverSocket, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
	if (iRet == SOCKET_ERROR)
	{
		cout << "bind(serverSocket, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) execute failed!" << endl;
		return -1;
	}

	//监听
	iRet = listen(serverSocket, CONNECT_NUM_MAX);
	if (iRet == SOCKET_ERROR)
	{
		cout << "listen(serverSocket," << CONNECT_NUM_MAX << ") execute failed!" << endl;
		return -1;
	}

	//等待连接_接收_发送
	SOCKADDR_IN clientAddr;
	int len = sizeof(SOCKADDR);
	//while (1)
	//{
	SOCKET connSocket = accept(serverSocket, (SOCKADDR*)&clientAddr, &len);
	if (connSocket == INVALID_SOCKET)
	{
		cout << "accept(serverSocket, (SOCKADDR*)&clientAddr, &len) execute failed!" << endl;
		return -1;
	}
	// 'inet_ntoa': Use inet_ntop() or InetNtop() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API warnings
	// http://stackoverflow.com/questions/26947496/deprecated-commands-in-visual-c 使用#include<Ws2tcpip.h>下的inet_ntop()
	// 属性——C/C++——常规——SDK检查 改为否
	// http://jingyan.baidu.com/article/1709ad8097e5904634c4f03e.html

	char sendBuf[100];
	memset(sendBuf, 0, sizeof(sendBuf));
	sprintf_s(sendBuf, "Welcome %s.  type date to get time,q to exit.", inet_ntoa(clientAddr.sin_addr));
	send(connSocket, sendBuf, strlen(sendBuf) + 1, 0);
	char recvBuf[100];
	while (true)
	{
		memset(recvBuf, 0, sizeof(recvBuf));
		recv(connSocket, recvBuf, 100, 0);
		printf("%s\n", recvBuf);
		if (strcmp(recvBuf, "date") == 0)
		{
			time_t t = time(0);
			//http://www.cnblogs.com/mfryf/archive/2012/02/13/2349360.html
			strftime(sendBuf, sizeof(sendBuf), "%Y/%m/%d %X %A 本年第 %j 天 %z", localtime(&t));

			send(connSocket, sendBuf, strlen(sendBuf) + 1, 0);
		}
		else if (strcmp(recvBuf, "q") == 0) {
			sprintf_s(sendBuf, "BYE");
			send(connSocket, sendBuf, strlen(sendBuf) + 1, 0);
			closesocket(connSocket);
			exit(0);
		}
		else {
			sprintf_s(sendBuf, "Type date to get time,q to exit.");
			send(connSocket, sendBuf, strlen(sendBuf) + 1, 0);
		}
	}

	//}

	system("pause");
	return 0;
}
 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
#include <iostream>
#include <string>
#include <winsock2.h>
using namespace std;
#define BYE "BYE"
#pragma comment(lib, "ws2_32.lib")
int main(int argc, char** argv)
{
	string host = "127.0.0.1";
	int port = 1234;
	if (argc == 3)
	{
		host = argv[1];
		port = atoi(argv[2]);
		if (port < 1024 || port>65534)
		{
			printf("Port error.Use %s host port\n", argv[0]);
		}
	}
	else if (argc != 1)
	{
		printf("Error. Use %s host port\n", argv[0]);
	}
	//加载套接字库
	WSADATA wsaData;
	int iRet = 0;
	iRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iRet != 0)
	{
		cout << "WSAStartup(MAKEWORD(2, 2), &wsaData) execute failed!" << endl;
		return -1;
	}
	if (2 != LOBYTE(wsaData.wVersion) || 2 != HIBYTE(wsaData.wVersion))
	{
		WSACleanup();
		cout << "WSADATA version is not correct!" << endl;
		return -1;
	}

	//创建套接字
	SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0);
	if (clientSocket == INVALID_SOCKET)
	{
		cout << "clientSocket = socket(AF_INET, SOCK_STREAM, 0) execute failed!" << endl;
		return -1;
	}

	//初始化服务器端地址族变量
	SOCKADDR_IN srvAddr;
	srvAddr.sin_addr.S_un.S_addr = inet_addr(host.c_str());
	srvAddr.sin_family = AF_INET;
	srvAddr.sin_port = htons(port);

	//连接服务器
	iRet = connect(clientSocket, (SOCKADDR*)&srvAddr, sizeof(SOCKADDR));
	if (0 != iRet)
	{
		cout << "connect(clientSocket, (SOCKADDR*)&srvAddr, sizeof(SOCKADDR)) execute failed!" << endl;
		return -1;
	}

	char recvBuf[100];
	char sendBuf[100];
	int count = 0;
	while (true)
	{
		memset(recvBuf, 0, sizeof(recvBuf));
		memset(sendBuf, 0, sizeof(sendBuf));
		//接收消息
		recv(clientSocket, recvBuf, 100, 0);
		printf("%s\n", recvBuf);
		if (strcmp(recvBuf, BYE) == 0)
		{
			//清理
			closesocket(clientSocket);
			WSACleanup();
			break;
		}
//		scanf("%d", &count);
//
//		if (count == 1)
//			sprintf(sendBuf, "date");
//		else if (count==0)
//			sprintf(sendBuf, "q");
//		else
//			sprintf(sendBuf, "unknow");
		string tmp;
		cin >> tmp;
		strcpy(sendBuf, tmp.c_str());

		count++;
		send(clientSocket, sendBuf, strlen(sendBuf) + 1, 0);
	}
	return 0;
}

第二题客户端代码没有变化。 第二题,多线程版本。 新建线程,可以指定新线程执行的函数以及要传递的参数。 要传递的参数,自定义的结构体。THREAD_DATA threadData1(connSocket, clientAddr, count++); 新建线程,ThreadProc是自己实现线程函数,threadData就是参数。HANDLE thread = CreateThread(nullptr, 0, ThreadProc, &threadData1, 0, nullptr);

点击显示/隐藏代码

  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
#include<iostream>
#include<WinSock2.h>
#include<ctime>
#include<vector>
#pragma comment(lib, "ws2_32.lib")
//#pragma comment(lib, "Ws2_32.lib")
#define CONNECT_NUM_MAX 10
#define _WINSOCK_DEPRECATED_NO_WARNINGS
using namespace std;

//http://blog.csdn.net/luoweifu/article/details/46835437
#define NAME_LINE   40
//定义线程函数传入参数的结构体
typedef struct __THREAD_DATA
{
	int id;
	SOCKET connSocket;
	SOCKADDR_IN clientAddr;

	__THREAD_DATA(SOCKET socket, SOCKADDR_IN client, int _id = 0)
	{
		connSocket = socket;
		clientAddr = client;
		id = _id;
	}
}THREAD_DATA;

//线程函数
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	THREAD_DATA* pThreadData = static_cast<THREAD_DATA*>(lpParameter);
	SOCKET connSocket = pThreadData->connSocket;
	SOCKADDR_IN clientAddr = pThreadData->clientAddr;
	int id = pThreadData->id;

	printf("客户端%d已连接\n", id);
	char sendBuf[100];
	memset(sendBuf, 0, sizeof(sendBuf));
	sprintf_s(sendBuf, "Welcome %s, You are No.%d.  type date to get time,q to exit.", inet_ntoa(clientAddr.sin_addr), id);
	send(connSocket, sendBuf, strlen(sendBuf) + 1, 0);
	char recvBuf[100];
	while (true)
	{
		memset(recvBuf, 0, sizeof(recvBuf));
		recv(connSocket, recvBuf, 100, 0);
		printf("[客户端%d命令] %s\n", id, recvBuf);
		if (strcmp(recvBuf, "date") == 0)
		{
			time_t t = time(0);
			//http://www.cnblogs.com/mfryf/archive/2012/02/13/2349360.html
			strftime(sendBuf, sizeof(sendBuf), "%Y/%m/%d %X %A 本年第 %j 天 %z", localtime(&t));
			send(connSocket, sendBuf, strlen(sendBuf) + 1, 0);
		}
		else if (strcmp(recvBuf, "q") == 0) {
			sprintf_s(sendBuf, "BYE");
			send(connSocket, sendBuf, strlen(sendBuf) + 1, 0);
			closesocket(connSocket);
			//CloseHandle()
			break;
		}
		else {
			sprintf_s(sendBuf, "Type date to get time,q to exit.");
			send(connSocket, sendBuf, strlen(sendBuf) + 1, 0);
		}
	}
	return 0L;
}

//http://www.js-code.com/C_yuyan/20150918/4488.html
int main(int argc, char** argv)
{
	int port = 1234;
	if (argc > 1)
	{
		port = atoi(argv[1]);
		if (port < 1024 || port>65534)
		{
			printf("Port error. Use %s port\n", argv[0]);
		}
	}
	//加载套接字库
	WSADATA wsaData;
	int iRet = 0;
	iRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iRet != 0)
	{
		cout << "WSAStartup(MAKEWORD(2, 2), &wsaData) execute failed!" << endl;;
		return -1;
	}
	if (2 != LOBYTE(wsaData.wVersion) || 2 != HIBYTE(wsaData.wVersion))
	{
		WSACleanup();
		cout << "WSADATA version is not correct!" << endl;
		return -1;
	}

	//创建套接字
	SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);
	if (serverSocket == INVALID_SOCKET)
	{
		cout << "serverSocket = socket(AF_INET, SOCK_STREAM, 0) execute failed!" << endl;
		return -1;
	}

	//初始化服务器地址族变量
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(port);
	//绑定
	iRet = bind(serverSocket, reinterpret_cast<SOCKADDR*>(&addrSrv), sizeof(SOCKADDR));
	if (iRet == SOCKET_ERROR)
	{
		cout << "bind(serverSocket, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) execute failed!" << endl;
		return -1;
	}

	//监听
	iRet = listen(serverSocket, CONNECT_NUM_MAX);
	if (iRet == SOCKET_ERROR)
	{
		cout << "listen(serverSocket," << CONNECT_NUM_MAX << ") execute failed!" << endl;
		return -1;
	}

	//等待连接_接收_发送
	SOCKADDR_IN clientAddr;
	int len = sizeof(SOCKADDR);
	vector<HANDLE> threads;
	int count = 0;
	cout << "Server is started\n";
	while (1)
	{
		SOCKET connSocket = accept(serverSocket, reinterpret_cast<SOCKADDR*>(&clientAddr), &len);
		if (connSocket == INVALID_SOCKET)
		{
			cout << "accept(serverSocket, (SOCKADDR*)&clientAddr, &len) execute failed!" << endl;
			return -1;
		}
		THREAD_DATA threadData1(connSocket, clientAddr, count++);
		HANDLE thread = CreateThread(nullptr, 0, ThreadProc, &threadData1, 0, nullptr);
		threads.push_back(thread);
	}
	return 0;
}
第二题,使用Select实现 参见:windows 和 linux 套接字中的 select 机制浅析
  • 1. 调用 FD_ZERO 来初始化套接字状态;
  • 2. 调用 FD_SET 将感兴趣的套接字描述符加入集合中(每次循环都要重新加入,因为 select 更新后,会将一些没有满足条件的套接字移除队列);
  • 3. 设置等待时间后,调用 select 函数 -- 更新套接字的状态;
  • 4. 调用 FD_ISSET,来判断套接字是否有相应状态,然后做相应操作,比如,如果套接字可读,就调用 recv 函数去接收数据。
关键技术:套接字队列和状态的表示与处理。

点击显示/隐藏代码

  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
//http://www.cnblogs.com/lidabo/p/3804411.html
//windows 和 linux 套接字中的 select 机制浅析

// server.cpp :   
// 程序中加入了套接字管理队列,这样管理起来更加清晰、方便,当然也可以不用这个东西  

#include "winsock.h"  
#include "stdio.h"
#include <ctime>
#include <iostream>
#pragma comment (lib,"wsock32.lib")  
#define MAX 64

//Socket列表
struct socket_list {
	SOCKET MainSock;
	int num;
	SOCKET sock_array[MAX];
};
//初始化列表
void init_list(socket_list *list)
{
	int i;
	list->MainSock = 0;
	list->num = 0;
	for (i = 0; i < MAX; i++) {
		list->sock_array[i] = 0;
	}
}
//插入一个Socket到列表
void insert_list(SOCKET s, socket_list *list)
{
	int i;
	for (i = 0; i < MAX; i++) {
		if (list->sock_array[i] == 0) {
			list->sock_array[i] = s;
			list->num += 1;
			break;
		}
	}
}
//从列表删除一个Socket
void delete_list(SOCKET s, socket_list *list)
{
	int i;
	for (i = 0; i < MAX; i++) {
		if (list->sock_array[i] == s) {
			list->sock_array[i] = 0;
			list->num -= 1;
			break;
		}
	}
}

void make_fdlist(socket_list *list, fd_set *fd_list)
{
	int i;
	//FD_SET(s,*set),向集合中加入一个套接口描述符
	//(如果该套接口描述符 s 没在集合中,并且数组中已经设置的个数小于最大个数时,就把该描述符加入到集合中,集合元素个数加 1)。
	//这里是将 s 的值直接放入数组中。
	FD_SET(list->MainSock, fd_list);
	for (i = 0; i < MAX; i++) {
		if (list->sock_array[i] > 0) {
			FD_SET(list->sock_array[i], fd_list);
		}
	}
}

/*
1. 调用 FD_ZERO 来初始化套接字状态;
2. 调用 FD_SET 将感兴趣的套接字描述符加入集合中(每次循环都要重新加入,因为 select 更新后,会将一些没有满足条件的套接字移除队列);
3. 设置等待时间后,调用 select 函数 -- 更新套接字的状态;
4. 调用 FD_ISSET,来判断套接字是否有相应状态,然后做相应操作,比如,如果套接字可读,就调用 recv 函数去接收数据。
关键技术:套接字队列和状态的表示与处理。*/

int main(int argc, char* argv[])
{
	int port = 1234;
	if (argc > 1)
	{
		port = atoi(argv[1]);
		if (port < 1024 || port>65534)
		{
			printf("Port error. Use %s port\n", argv[0]);
		}
	}
	//s是Server,sock是客户端
	SOCKET s, sock;
	struct sockaddr_in ser_addr, remote_addr;
	int len;
	char buf[128];
	WSAData wsa;
	int retval;
	struct socket_list sock_list;
	fd_set readfds, writefds, exceptfds;
	timeval timeout;        //select 的最多等待时间,防止一直等待  
	int i;
	unsigned long arg;

	WSAStartup(0x101, &wsa);
	s = socket(AF_INET, SOCK_STREAM, 0);
	ser_addr.sin_family = AF_INET;
	ser_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	ser_addr.sin_port = htons(port);
	i = bind(s, reinterpret_cast<sockaddr*>(&ser_addr), sizeof(ser_addr));
	if (i == SOCKET_ERROR)
	{
		std::cout << "bind error\n";
		return -1;
	}
	i = listen(s, 5);
	if (i == SOCKET_ERROR)
	{
		std::cout << "listen error\n";
		return -1;
	}
	timeout.tv_sec = 5;     // 如果套接字集合中在 1s 内没有数据,select 就会返回,超时 select 返回 0  
	timeout.tv_usec = 0;
	init_list(&sock_list);

	//FD_ZERO(*set),是把集合清空(初始化为 0,确切的说,是把集合中的元素个数初始化为 0,并不修改描述符数组). 
	//使用集合前,必须用 FD_ZERO 初始化,否则集合在栈上作为自动变量分配时,fd_set 分配的将是随机值,导致不可预测的问题。
	FD_ZERO(&readfds);
	FD_ZERO(&writefds);
	FD_ZERO(&exceptfds);

	sock_list.MainSock = s;
	arg = 1;
	ioctlsocket(sock_list.MainSock, FIONBIO, &arg);
	std::cout << "Server is started\n";
	while (1) {
		make_fdlist(&sock_list, &readfds);
		//make_fdlist(&sock_list,&writefds);  
		//make_fdlist(&sock_list,&exceptfds);  

		retval = select(0, &readfds, &writefds, &exceptfds, &timeout);// 超过这个时间,就不阻塞在这里,返回一个 0 值。  
		if (retval == SOCKET_ERROR) {
			retval = WSAGetLastError();
			printf("error code=%d", retval);
			break;
		}
		if (retval == 0) {
			//printf("select() is time-out! There is no data or new-connect coming!\n");
			continue;
		}

		char sendBuf[128];
		//FD_ISSET(s,*set),检查描述符是否在集合中,如果在集合中返回非 0 值,否则返回 0.
		//它的宏定义并没有给出具体实现,但实现的思路很简单,就是搜索集合,判断套接字 s 是否在数组中。
		if (FD_ISSET(sock_list.MainSock, &readfds)) {
			len = sizeof(remote_addr);
			sock = accept(sock_list.MainSock, reinterpret_cast<sockaddr*>(&remote_addr), &len);
			if (sock == SOCKET_ERROR)
				continue;
			printf("accept a connection\n");
			insert_list(sock, &sock_list);

			memset(sendBuf, 0, sizeof(sendBuf));
			sprintf_s(sendBuf, "Welcome.  type date to get time,q to exit.");
			send(sock, sendBuf, strlen(sendBuf) + 1, 0);

		}
		for (i = 0; i < MAX; i++) {
			if (sock_list.sock_array[i] == 0)
				continue;
			sock = sock_list.sock_array[i];

			if (FD_ISSET(sock, &readfds)) {
				//接受客户端数据,返回数据长度
				retval = recv(sock, buf, 128, 0);
				if (retval == 0) {
					closesocket(sock);
					printf("close a socket\n");
					delete_list(sock, &sock_list);
					continue;
				}
				if (retval == -1) {
					retval = WSAGetLastError();
					if (retval == WSAEWOULDBLOCK)
						continue;
					closesocket(sock);
					printf("close a socket\n");
					delete_list(sock, &sock_list);   // 连接断开后,从队列中移除该套接字  
					continue;
				}
				buf[retval] = 0;
				printf("->%s\n", buf);

				memset(sendBuf, 0, sizeof(sendBuf));
				if (strcmp(buf, "date") == 0)
				{
					time_t t = time(nullptr);
					strftime(sendBuf, sizeof(sendBuf), "%Y/%m/%d %X %A 本年第 %j 天 %z", localtime(&t));
					send(sock, sendBuf, strlen(sendBuf) + 1, 0);
				}
				else if (strcmp(buf, "q") == 0)
				{
					sprintf_s(sendBuf, "BYE");
					send(sock, sendBuf, strlen(sendBuf) + 1, 0);
					closesocket(sock);
					printf("close a socket\n");
					delete_list(sock, &sock_list);
				}
				else
				{
					sprintf_s(sendBuf, "Type date to get time,q to exit.");
					send(sock, sendBuf, strlen(sendBuf) + 1, 0);
				}
			}
			//if(FD_ISSET(sock,&writefds)){  
			//}  
			//if(FD_ISSET(sock,&exceptfds)){  

		}
		FD_ZERO(&readfds);
		FD_ZERO(&writefds);
		FD_ZERO(&exceptfds);
	}
	closesocket(sock_list.MainSock);
	WSACleanup();
	return 0;
}

第三题,原始套接字。 首先定义协议头部的结构体,然后对收到的数据进行解析即可。 具体细节可以看代码,注释还算详细。

点击显示/隐藏代码

  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
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
#define RAW_SOCKET_

#ifdef RAW_SOCKET_
#include <WinSock2.h>
#include <iostream>
#pragma comment(lib, "ws2_32.lib")

#define MAXPOCKETSIZE 65535

typedef struct _iphdr				//IP首部 
{
	unsigned char	h_verlen;		//4+4IP版本号 
	unsigned char	tos;			//8TOS 
	unsigned short	total_len;		//16 
	unsigned short	ident;			//16 
	unsigned short	frag_and_flags;	//3 
	unsigned char	ttl;			//8 TTL 
	unsigned char	proto;			//8 (TCP, UDP ) 
	unsigned short	checksum;		//16IP首部校验和 
	unsigned long	sourceIP;		//32IP地址 
	unsigned long	destIP;			//32IP地址 
}IP_HEADER;

typedef struct _udphdr			//UDP首部
{
	unsigned short uh_sport;    //16
	unsigned short uh_dport;    //16
	unsigned short uh_len;		//16UDP包长度
	unsigned short uh_sum;		//16
}UDP_HEADER;

typedef struct _tcphdr			//TCP首部 
{
	unsigned short	th_sport;	//16 
	unsigned short	th_dport;	//16 
	unsigned long	th_seq;		//32 
	unsigned long	th_ack;		//32 
	char			th_lenres;	//4/6 
	char			th_flag;	//6
	unsigned short	th_win;		//16
	unsigned short	th_sum;		//16
	unsigned short	th_urp;		//16
}TCP_HEADER;

typedef struct _icmphdr {
	unsigned char  icmp_type;
	unsigned char  icmp_code; /* type sub code */
	unsigned short icmp_cksum;
	unsigned short icmp_id;
	unsigned short icmp_seq;
	/* This is not the std header, but we reserve space for time */
	unsigned long icmp_timestamp;
}ICMP_HEADER;

using namespace std;

void DecodeICMPPacket(char* pData)
{
	ICMP_HEADER *p_icmp_header = reinterpret_cast<ICMP_HEADER*>(pData);
	cout << "ICMP信息\tICMP Type:" << p_icmp_header->icmp_type << "\t\tCode:" << p_icmp_header->icmp_code << endl;
	switch (p_icmp_header->icmp_type)
	{
	case 0:cout << "回显答复Echo Response\n"; break;
	case 8:cout << "请求回显Echo Request\n"; break;
	case 3:cout << "目标不可达Destination Unreachable\n"; break;
	case 11:cout << "数据包超时Datagram Timeout(TTL=0)\n"; break;
	}
}

void DecodeUDPPacket(char* pData) {
	UDP_HEADER *p_udp_header = reinterpret_cast<UDP_HEADER*>(pData);
	cout << "UDP信息\t源端口=" << ntohs(p_udp_header->uh_sport)
		<< "\t\t目的端口=" << ntohs(p_udp_header->uh_dport) << endl;
	cout << (pData + 8);
}

void DecodeTCPPacket(char* pData)
{
	TCP_HEADER *p_tcp_header = reinterpret_cast<TCP_HEADER*>(pData);
	cout << "TCP信息\t源端口=" << ntohs(p_tcp_header->th_sport)
		<< "\t\t目的端口=" << ntohs(p_tcp_header->th_dport) << endl;
	cout << (pData + 20);
}
void DecodeIPPacket(char *pData)
{
	IP_HEADER *p_ip_header = reinterpret_cast<IP_HEADER*>(pData);
	in_addr source, dest;
	char szSourceIP[32], szDestIP[32];
	source.S_un.S_addr = p_ip_header->sourceIP;
	dest.S_un.S_addr = p_ip_header->destIP;
	strcpy(szSourceIP, inet_ntoa(source));
	strcpy(szDestIP, inet_ntoa(dest));
	cout << "IP信息\t";
	cout << "源地址=" << szSourceIP << "\t目标地址=" << szDestIP << endl;
	int nHeaderLen = (p_ip_header->h_verlen & 0xf)*sizeof(ULONG);

	switch (p_ip_header->proto)
	{
	case IPPROTO_TCP:DecodeTCPPacket(pData + nHeaderLen); break;
	case IPPROTO_UDP:DecodeUDPPacket(pData + nHeaderLen); break;
	case IPPROTO_ICMP:DecodeICMPPacket(pData + nHeaderLen); break;
	default:cout << "协议号:" << p_ip_header->proto << endl;
	}
}

int main(int argc, char* argv[])
{
	//
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 0), &wsaData);

	SOCKET sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
	if (sock == INVALID_SOCKET)
	{
		printf("[Error Code=%d]Create Raw Socket Error.Access Denied.创建原始套接字失败,需要管理员权限。\n",
			WSAGetLastError());
		return -1;
	}
	//
	char sHostName[256];
	SOCKADDR_IN addr_in;
	struct hostent *hptr;
	gethostname(sHostName, sizeof(sHostName));
	if ((hptr = gethostbyname(sHostName)) == nullptr)
	{
		cout << "获取本地IP地址出错 Error Code=" << WSAGetLastError() << endl;
		WSACleanup();
		return -1;
	}
	//IP地址
	char **pptr = hptr->h_addr_list;
	cout << "本机 IP 列表:\n";
	while (*pptr != nullptr)
	{
		cout << inet_ntoa(*reinterpret_cast<struct in_addr*>(*pptr)) << endl;
		pptr++;
	}
	cout << "输入要监听的接口的IP地址:\n>";
	char snfIP[20];
	cin.getline(snfIP, sizeof(snfIP));

	// SOCKADDR_IN结构 
	addr_in.sin_family = AF_INET;
	addr_in.sin_port = htons(0);
	addr_in.sin_addr.S_un.S_addr = inet_addr(snfIP);

	//  sock  
	if (bind(sock, reinterpret_cast<PSOCKADDR>(&addr_in), sizeof(addr_in)) == SOCKET_ERROR)
	{
		printf("[Error Code=%d]Bind Error.绑定错误\n", WSAGetLastError());
		closesocket(sock);
		WSACleanup();
		return -1;
	}

	//.dwValue为输入输出参数10 
	DWORD dwValue = 1;

	//  SOCK_RAW SIO_RCVALL便IP包SIO_RCVALL 
	//  #define SIO_RCVALL _WSAIOW(IOC_VENDOR,1) 
#define SIO_RCVALL _WSAIOW(IOC_VENDOR,1)
	int ioctlsckterr = ioctlsocket(sock, SIO_RCVALL, &dwValue);
	if (ioctlsckterr != NO_ERROR) {
		printf("[Error Code=%d]Error at ioctlsocket()设置网卡为混杂模式出错\n", WSAGetLastError());
		closesocket(sock);
		WSACleanup();
		return -1;
	}

	char buff[MAXPOCKETSIZE];
	int nRet;
	while (1)
	{
		memset(buff, 0, sizeof(buff));
		nRet = recv(sock, buff, sizeof(buff), 0);
		if (nRet <= 0)
		{
			cout << "[Error Code=" << WSAGetLastError() << "]抓取数据出错." << endl;
			break;
		}
		cout << "\n-------------------------------------------------------------------------------\n";
		DecodeIPPacket(buff);
	}
	closesocket(sock);
	WSACleanup();
	return 0;
}

#endif

#ifdef PING____
/*http://download.csdn.net/detail/geoff08zhang/4571358*/
/*************************************************************************
*
* Copyright (c) 2002-2005 by Zhang Huiyong All Rights Reserved
*
* FILENAME:  Ping.c
*
* PURPOSE :  Ping .
*
* AUTHOR  :  
*
* BOOK    :  <<WinSock网络编程经络>>
*
**************************************************************************/

#include <stdio.h>
#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")    /* WinSock使用的库函数 */

/* ICMP  */
#define ICMP_TYPE_ECHO          8
#define ICMP_TYPE_ECHO_REPLY    0

#define ICMP_MIN_LEN            8  /* ICMP 最小长度, 只有首部 */
#define ICMP_DEF_COUNT          4  /* 缺省数据次数 */
#define ICMP_DEF_SIZE          32  /* 缺省数据长度 */
#define ICMP_DEF_TIMEOUT     1000  /* 缺省超时时间, 毫秒 */
#define ICMP_MAX_SIZE       65500  /* 最大数据长度 */

/* IP  -- RFC 791 */
struct ip_hdr
{
	unsigned char vers_len;     /*  */
	unsigned char tos;          /*  */
	unsigned short total_len;   /*  */
	unsigned short id;          /*  */
	unsigned short frag;        /*  */
	unsigned char ttl;          /*  */
	unsigned char proto;        /*  */
	unsigned short checksum;    /*  */
	unsigned int sour;          /*  IP  */
	unsigned int dest;          /*  IP  */
};

/* ICMP  -- RFC 792 */
struct icmp_hdr
{
	unsigned char type;         /*  */
	unsigned char code;         /*  */
	unsigned short checksum;    /*  */
	unsigned short id;          /*  */
	unsigned short seq;         /*  */

								/*  ICMP ,  */
	unsigned long timestamp;
};

struct icmp_user_opt
{
	unsigned int  persist;  /*  Ping            */
	unsigned int  count;    /*  ECHO  */
	unsigned int  size;     /*        */
	unsigned int  timeout;  /*    */
	char          *host;    /*      */
	unsigned int  send;     /*      */
	unsigned int  recv;     /*      */
	unsigned int  min_t;    /*      */
	unsigned int  max_t;    /*      */
	unsigned int  total_t;  /*  */
};

/*  */
const char icmp_rand_data[] = "abcdefghigklmnopqrstuvwxyz0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";

struct icmp_user_opt user_opt_g = {
	0, ICMP_DEF_COUNT, ICMP_DEF_SIZE, ICMP_DEF_TIMEOUT, NULL,
	0, 0, 0xFFFF, 0
};

unsigned short ip_checksum(unsigned short *buf, int buf_len);

/**************************************************************************
*
* :  ICMP .
*
* : [IN, OUT] icmp_data, ICMP ;
*           [IN] data_size, icmp_data ;
*           [IN] sequence, .
*
*   : void.
*
**************************************************************************/
void icmp_make_data(char *icmp_data, int data_size, int sequence)
{
	struct icmp_hdr *icmp_hdr;
	char *data_buf;
	int data_len;
	int fill_count = sizeof(icmp_rand_data) / sizeof(icmp_rand_data[0]);

	/*  ICMP  */
	data_buf = icmp_data + sizeof(struct icmp_hdr);
	data_len = data_size - sizeof(struct icmp_hdr);

	while (data_len > fill_count)
	{
		memcpy(data_buf, icmp_rand_data, fill_count);
		data_len -= fill_count;
	}

	if (data_len > 0)
		memcpy(data_buf, icmp_rand_data, data_len);

	/*  ICMP  */
	icmp_hdr = (struct icmp_hdr *)icmp_data;

	icmp_hdr->type = ICMP_TYPE_ECHO;
	icmp_hdr->code = 0;
	icmp_hdr->id = (unsigned short)GetCurrentProcessId();
	icmp_hdr->checksum = 0;
	icmp_hdr->seq = sequence;
	icmp_hdr->timestamp = GetTickCount();

	icmp_hdr->checksum = ip_checksum((unsigned short*)icmp_data, data_size);
}

/**************************************************************************
*
* : .
*
* : [IN] buf, ;
*           [IN] buf_len, buf ;
*           [IN] from, .
*
*   :  0,  -1.
*
**************************************************************************/
int icmp_parse_reply(char *buf, int buf_len, struct sockaddr_in *from)
{
	struct ip_hdr *ip_hdr;
	struct icmp_hdr *icmp_hdr;
	unsigned short hdr_len;
	int icmp_len;
	unsigned long trip_t;

	ip_hdr = (struct ip_hdr *)buf;
	hdr_len = (ip_hdr->vers_len & 0xf) << 2; /* IP  */

	if (buf_len < hdr_len + ICMP_MIN_LEN)
	{
		printf("[Ping] Too few bytes from %s\n", inet_ntoa(from->sin_addr));
		return -1;
	}

	icmp_hdr = (struct icmp_hdr *)(buf + hdr_len);
	icmp_len = ntohs(ip_hdr->total_len) - hdr_len;

	/*  */
	if (ip_checksum((unsigned short *)icmp_hdr, icmp_len))
	{
		printf("[Ping] icmp checksum error!\n");
		return -1;
	}

	/*  ICMP  */
	if (icmp_hdr->type != ICMP_TYPE_ECHO_REPLY)
	{
		printf("[Ping] not echo reply : %d\n", icmp_hdr->type);
		return -1;
	}

	/*  ICMP  ID */
	if (icmp_hdr->id != (unsigned short)GetCurrentProcessId())
	{
		printf("[Ping] someone else's message!\n");
		return -1;
	}

	/*  */
	trip_t = GetTickCount() - icmp_hdr->timestamp;
	buf_len = ntohs(ip_hdr->total_len) - hdr_len - ICMP_MIN_LEN;
	printf("%d bytes from %s:", buf_len, inet_ntoa(from->sin_addr));
	printf(" icmp_seq = %d  time: %d ms\n", icmp_hdr->seq, trip_t);

	user_opt_g.recv++;
	user_opt_g.total_t += trip_t;

	/*  */
	if (user_opt_g.min_t > trip_t)
		user_opt_g.min_t = trip_t;

	if (user_opt_g.max_t < trip_t)
		user_opt_g.max_t = trip_t;

	return 0;
}

/**************************************************************************
*
* : , .
*
* : [IN] icmp_soc, .
*
*   :  0,  -1.
*
**************************************************************************/
int icmp_process_reply(SOCKET icmp_soc)
{
	struct sockaddr_in from_addr;
	int result, data_size = user_opt_g.size;
	int from_len = sizeof(from_addr);
	char *recv_buf;

	data_size += sizeof(struct ip_hdr) + sizeof(struct icmp_hdr);
	recv_buf = static_cast<char*>(malloc(data_size));

	/*  */
	result = recvfrom(icmp_soc, recv_buf, data_size, 0,
		(struct sockaddr*)&from_addr, &from_len);
	if (result == SOCKET_ERROR)
	{
		if (WSAGetLastError() == WSAETIMEDOUT)
			printf("timed out\n");
		else
			printf("[PING] recvfrom_ failed: %d\n", WSAGetLastError());

		return -1;
	}

	result = icmp_parse_reply(recv_buf, result, &from_addr);
	free(recv_buf);

	return result;
}

/**************************************************************************
*
* :  ECHO .
*
* : [IN] prog_name, ;
*
*   : void.
*
**************************************************************************/
void icmp_help(char *prog_name)
{
	char *file_name;

	file_name = strrchr(prog_name, '\\');
	if (file_name != NULL)
		file_name++;
	else
		file_name = prog_name;

	/*  */
	printf(" usage:     %s host_address [-t] [-n count] [-l size] "
		"[-w timeout]\n", file_name);
	printf(" -t         Ping the host until stopped.\n");
	printf(" -n count   the count to send ECHO\n");
	printf(" -l size    the size to send data\n");
	printf(" -w timeout timeout to wait the reply\n");
	exit(1);
}

/**************************************************************************
*
* : , .
*
* : [IN] argc, ;
*           [IN] argv, .
*
*   : void.
*
**************************************************************************/
void icmp_parse_param(int argc, char **argv)
{
	int i;

	for (i = 1; i < argc; i++)
	{
		if ((argv[i][0] != '-') && (argv[i][0] != '/'))
		{
			/*  */
			if (user_opt_g.host)
				icmp_help(argv[0]);
			else
			{
				user_opt_g.host = argv[i];
				continue;
			}
		}

		switch (tolower(argv[i][1]))
		{
		case 't':   /*  Ping */
			user_opt_g.persist = 1;
			break;

		case 'n':   /*  */
			i++;
			user_opt_g.count = atoi(argv[i]);
			break;

		case 'l':   /*  */
			i++;
			user_opt_g.size = atoi(argv[i]);
			if (user_opt_g.size > ICMP_MAX_SIZE)
				user_opt_g.size = ICMP_MAX_SIZE;
			break;

		case 'w':   /*  */
			i++;
			user_opt_g.timeout = atoi(argv[i]);
			break;

		default:
			icmp_help(argv[0]);
			break;
		}
	}
}

int main(int argc, char **argv)
{
	WSADATA wsaData;
	SOCKET icmp_soc;
	struct sockaddr_in dest_addr;
	struct hostent *host_ent = NULL;

	int result, data_size, send_len;
	unsigned int i, timeout, lost;
	char *icmp_data;
	unsigned int ip_addr = 0;
	unsigned short seq_no = 0;

	if (argc < 2)
		icmp_help(argv[0]);

	icmp_parse_param(argc, argv);
	WSAStartup(MAKEWORD(2, 0), &wsaData);

	/*  */
	ip_addr = inet_addr(user_opt_g.host);
	if (ip_addr == INADDR_NONE)
	{
		host_ent = gethostbyname(user_opt_g.host);
		if (!host_ent)
		{
			printf("[PING] Fail to resolve %s\n", user_opt_g.host);
			return -1;
		}

		memcpy(&ip_addr, host_ent->h_addr_list[0], host_ent->h_length);
	}

	icmp_soc = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
	if (icmp_soc == INVALID_SOCKET)
	{
		printf("[PING] socket() failed: %d\n", WSAGetLastError());
		return -1;
	}

	/* ,  */
	timeout = user_opt_g.timeout;
	result = setsockopt(icmp_soc, SOL_SOCKET, SO_RCVTIMEO,
		(char*)&timeout, sizeof(timeout));

	timeout = 1000;
	result = setsockopt(icmp_soc, SOL_SOCKET, SO_SNDTIMEO,
		(char*)&timeout, sizeof(timeout));

	memset(&dest_addr, 0, sizeof(dest_addr));
	dest_addr.sin_family = AF_INET;
	dest_addr.sin_addr.s_addr = ip_addr;

	data_size = user_opt_g.size + sizeof(struct icmp_hdr) - sizeof(long);
	icmp_data = static_cast<char*>(malloc(data_size));

	if (host_ent)
		printf("Ping %s [%s] with %d bytes data\n", user_opt_g.host,
			inet_ntoa(dest_addr.sin_addr), user_opt_g.size);
	else
		printf("Ping [%s] with %d bytes data\n", inet_ntoa(dest_addr.sin_addr),
			user_opt_g.size);

	/*  */
	for (i = 0; i < user_opt_g.count; i++)
	{
		icmp_make_data(icmp_data, data_size, seq_no++);

		send_len = sendto(icmp_soc, icmp_data, data_size, 0,
			(struct sockaddr*)&dest_addr, sizeof(dest_addr));
		if (send_len == SOCKET_ERROR)
		{
			if (WSAGetLastError() == WSAETIMEDOUT)
			{
				printf("[PING] sendto is timeout\n");
				continue;
			}

			printf("[PING] sendto failed: %d\n", WSAGetLastError());
			break;
		}

		user_opt_g.send++;
		result = icmp_process_reply(icmp_soc);

		user_opt_g.persist ? i-- : i; /*  Ping */
		Sleep(1000); /*  1  */
	}

	lost = user_opt_g.send - user_opt_g.recv;

	/*  */
	printf("\nStatistic :\n");
	printf("    Packet : sent = %d, recv = %d, lost = %d (%3.f%% lost)\n",
		user_opt_g.send, user_opt_g.recv, lost, (float)lost * 100 / user_opt_g.send);

	if (user_opt_g.recv > 0)
	{
		printf("Roundtrip time (ms)\n");
		printf("    min = %d ms, max = %d ms, avg = %d ms\n", user_opt_g.min_t,
			user_opt_g.max_t, user_opt_g.total_t / user_opt_g.recv);
	}

	free(icmp_data);
	closesocket(icmp_soc);
	WSACleanup();

	return 0;
}

/**************************************************************************
*
* : .
*
* : [IN] buf, ;
*           [IN] buf_len, buf .
*
*   : .
*
**************************************************************************/
unsigned short ip_checksum(unsigned short *buf, int buf_len)
{
	unsigned long checksum = 0;

	while (buf_len > 1)
	{
		checksum += *buf++;
		buf_len -= sizeof(unsigned short);
	}

	if (buf_len)
	{
		checksum += *(unsigned char *)buf;
	}

	checksum = (checksum >> 16) + (checksum & 0xffff);
	checksum += (checksum >> 16);

	return (unsigned short)(~checksum);
}
#endif

第四题,传输文件。 设置各种消息类型,采用一问一答形式,比如刚建立连接时:
服务器:[MSG_CMD]我支持这些命令。 客户端:[MSG_FILENAME]输入一个命令< 我要a.txt文件> 服务器:[MSG_FILE_LENGTH]你怎么知道我有这个文件呢,返回文件长度。 客户端:[MSG_CLIENT_READY]收到长度表示服务器有这个文件,在客户端本地新建(或覆盖)这个文件。我准备好了,你从位置0开始传输内容吧。 服务器:[MSG_FILE]从位置0开始传输一个文件块。(假设一个块长1024) 客户端:[MSG_CLIENT_READY]我准备好了,你从1024开始传输吧。 服务器:[MSG_FILE]从1024开始传输文件块。 客户端:[MSG_FILE_SUCC]客户端计算知道文件传输完毕,就说接收完毕。 服务器:[MSG_FILE_CMD]哦,我知道了。我支持这些命令。
一轮结束。错误处理看代码吧。

点击显示/隐藏代码

  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
#pragma once
#ifndef SEND_FILE_HEADER__
#define SEND_FILE_HEADER__

#define MAX_PACKET_SIZE				10240	// 数据包的最大长度, 单位是 sizeof(char)
#define	FILENAME_LENGTH				256		//文件名长度
#define MAX_FILE_SIZE				((MAX_PACKET_SIZE)-3*sizeof(long)-FILENAME_LENGTH-sizeof(char))
#define CMD_LENGTH					256		//命令描述的最大长度
// 各种消息的宏定义  
#define INVALID_MSG					-1  // 无效的消息标识
#define MSG_CMD						1	// 客户端刚连接时,服务器发送支持的命令
#define MSG_FILENAME				2   // 文件的名称  
#define MSG_FILELENGTH				3   // 传送文件的长度  
#define MSG_CLIENT_READY			4   // 客户端准备接收文件  
#define MSG_FILE					5   // 传送文件  
#define MSG_SEND_FILE_SUCCESS		6   // 传送文件成功  
#define MSG_OPENFILE_ERROR			7   // 打开文件失败, 可能是文件路径错误找不到文件等原因  
#define MSG_FILE_ALREADYEXIT_ERROR	8   // 要保存的文件已经存在了 
#define MSG_BYE						9	//断开
#define MSG_HELLO					10	//客户端发送Hello

#pragma pack(1)
/*消息头*/
struct tMSG_HEADER
{
	/*消息标识*/
	char cMsgId;
	tMSG_HEADER(char id = INVALID_MSG) :cMsgId(id) {};
};

struct tMSG_HELLO :tMSG_HEADER
{
	tMSG_HELLO() :tMSG_HEADER(MSG_HELLO) {}
};
/*服务器发送的命令*/
struct tMSG_CMD :tMSG_HEADER
{
	char commad[CMD_LENGTH];
	tMSG_CMD() :tMSG_HEADER(MSG_CMD) {}
};

/*传送的是文件名*/
struct tMSG_FILENAME :tMSG_HEADER
{
	/*文件名*/
	char szFileName[FILENAME_LENGTH];
	tMSG_FILENAME() :tMSG_HEADER(MSG_FILENAME) {}
};

/*传送的是文件长度*/
struct tMSG_FILE_LENGTH :tMSG_HEADER
{
	/*文件长度*/
	long lLength;
	char szFileName[FILENAME_LENGTH];
	tMSG_FILE_LENGTH(long l) :tMSG_HEADER(MSG_FILELENGTH), lLength(l) {}
};

/*客户端准备好了*/
struct tMSG_CLIENT_READY :tMSG_HEADER
{
	long lLastPosition, lLength;
	char szFileName[FILENAME_LENGTH];
	tMSG_CLIENT_READY(long l, long len) :tMSG_HEADER(MSG_CLIENT_READY), lLastPosition(l), lLength(len) {}
};

/*接下来传送的是文件*/
struct tMSG_FILE :tMSG_HEADER
{
	union // 采用 union 保证了数据包的大小不大于 MAX_PACKET_SIZE * sizeof(char)
	{
		char szBuff[MAX_PACKET_SIZE - sizeof(char)];///<strong>父结构体还有一个char类型的MsgId!!!</strong>
		struct
		{
			long lStart;
			long lSize;
			long lFileLength;
			char szFileName[FILENAME_LENGTH];
			char szBuff[MAX_FILE_SIZE];
		}tFile;
	};
	tMSG_FILE() : tMSG_HEADER(MSG_FILE) {}
};

/*传送文件成功*/
struct tMSG_SEND_FILE_SUCC :tMSG_HEADER
{
	char szFileName[FILENAME_LENGTH];
	tMSG_SEND_FILE_SUCC() :tMSG_HEADER(MSG_SEND_FILE_SUCCESS) {}
};

/*错误消息*/
struct tMSG_ERROR :tMSG_HEADER
{
	tMSG_ERROR(char ErrType) :tMSG_HEADER(ErrType) {}
};

/*断开*/
struct tMSG_BYE :tMSG_HEADER
{
	tMSG_BYE() :tMSG_HEADER(MSG_BYE) {}
};
#pragma pack()
#endif


  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
/*
1.在前三个实验的基础上,将其改造为一个能传输指定文件名称的点对点文件传输软件
2.设计并实现一个支持多个客户端的文件传输服务器
3.客户端等待键盘输入文件名称,然后将文件名称传输给服务器,
  服务器在预先设置好的文件夹下查找该文件,如果发现同名文件,开始传输回客户端,
  客户端接收完文件后将文件以输入的文件名称保存在本地某个目录即可,否则告诉客户端文件不存在。
*/
#include <iostream>
#include <fstream>
#include <WinSock2.h>
#include "header.h"
#pragma comment(lib, "ws2_32.lib")
#define CONNECT_NUM_MAX 10

using namespace std;

/*发送服务器支持的命令*/
void SendCmd(SOCKET sClient)
{
	tMSG_CMD cmd;
	sprintf_s(cmd.commad, "Type filename to get a file, /q to exit.\n>");
	send(sClient, reinterpret_cast<char*>(&cmd), sizeof(cmd), 0);
}

/*客户端输入的是文件名,返回文件长度,或文件不存在错误*/
void SendFileLength(SOCKET sClient, tMSG_HEADER *p_msg_header, int id)
{
	tMSG_FILENAME *p_msg_filename = static_cast<tMSG_FILENAME*>(p_msg_header);
	ifstream filein(p_msg_filename->szFileName, ios::in | ios::binary);
	if (!filein)
	{
		//文件不存在
		cout << "[Client " << id << "]<" << p_msg_filename->szFileName << "> File Not Found!\n";
		tMSG_ERROR ErrMsg(MSG_OPENFILE_ERROR);
		send(sClient, reinterpret_cast<char*>(&ErrMsg), sizeof(ErrMsg), 0);
	}
	else
	{
		//返回文件长度
		filein.seekg(0, ios::end);
		long length = filein.tellg();
		cout << "[Client " << id << "]<" << p_msg_filename->szFileName << "> File Length = " << length << endl;
		tMSG_FILE_LENGTH FileLength(length);
		strcpy(FileLength.szFileName, p_msg_filename->szFileName);
		send(sClient, reinterpret_cast<char*>(&FileLength), sizeof(FileLength), 0);
	}
	filein.close();
}

/*传送文件*/
void SendFile(SOCKET sClient, tMSG_HEADER *p_msg_header, int id)
{
	tMSG_CLIENT_READY *p_msg_client_ready = static_cast<tMSG_CLIENT_READY*>(p_msg_header);
	ifstream fin(p_msg_client_ready->szFileName, ios::in | ios::binary);
	if (!fin) { cout << "[Client " << id << "]<" << p_msg_client_ready->szFileName << "> Error Open File!\n"; }

	/*从上次结束的地方开始传送*/
	fin.seekg(p_msg_client_ready->lLastPosition, ios::beg);

	tMSG_FILE tSendFile;
	tSendFile.tFile.lStart = p_msg_client_ready->lLastPosition;
	tSendFile.tFile.lFileLength = p_msg_client_ready->lLength;
	strcpy(tSendFile.tFile.szFileName, p_msg_client_ready->szFileName);

	if (tSendFile.tFile.lFileLength - tSendFile.tFile.lStart > MAX_FILE_SIZE)
	{	//要传送的长度大于一次传送的长度
		tSendFile.tFile.lSize = MAX_FILE_SIZE;
	}
	else
	{	//要传送的文件长度可在这一次内传送完成
		tSendFile.tFile.lSize = tSendFile.tFile.lFileLength - tSendFile.tFile.lStart;
	}

	fin.read(tSendFile.tFile.szBuff, tSendFile.tFile.lSize);
	fin.close();
	//	cout << (int)tSendFile.cMsgId;
//	tMSG_HEADER *p_header = static_cast<tMSG_HEADER*>(&tSendFile);
//	cout << (int)p_header->cMsgId;
	send(sClient, reinterpret_cast<char*>(&tSendFile), sizeof(tSendFile), 0);
	cout << "[Client " << id << "]<" << tSendFile.tFile.szFileName << "> 已发送" << tSendFile.tFile.lSize + tSendFile.tFile.lStart << "/" << tSendFile.tFile.lFileLength << endl;
}

void SendSucc(SOCKET socket, tMSG_HEADER *p_msg_header, int id)
{
	tMSG_SEND_FILE_SUCC *succ = static_cast<tMSG_SEND_FILE_SUCC*>(p_msg_header);
	cout << "[Client " << id << "]<" << succ->szFileName << "> Send File Success!\n";
	SendCmd(socket);
}

/*处理消息,当要断开连接时返回false*/
bool ProcessMsg(SOCKET sClient, int id)
{
	char recvBuf[MAX_PACKET_SIZE];
	//接收消息
	int nRecv = recv(sClient, recvBuf, sizeof(recvBuf) + 1, 0);
	if (nRecv > 0)
	{
		recvBuf[nRecv] = '\0';
	}
	//解析
	tMSG_HEADER *p_msg_header = reinterpret_cast<tMSG_HEADER*>(recvBuf);
	switch (p_msg_header->cMsgId)
	{
	case MSG_HELLO:					SendCmd(sClient);							break;
	case MSG_FILENAME:				SendFileLength(sClient, p_msg_header, id);	break;
	case MSG_CLIENT_READY:			SendFile(sClient, p_msg_header, id);		break;
	case MSG_SEND_FILE_SUCCESS:		SendSucc(sClient, p_msg_header, id);		break;
	case MSG_FILE_ALREADYEXIT_ERROR:break;//覆盖客户端文件,因此这个暂时不需处理
	case MSG_BYE:return false;
	}
	return true;
}

//http://blog.csdn.net/luoweifu/article/details/46835437
#define NAME_LINE   40
//定义线程函数传入参数的结构体
typedef struct __THREAD_DATA
{
	int id;
	SOCKET connSocket;
	SOCKADDR_IN clientAddr;

	__THREAD_DATA(SOCKET socket, SOCKADDR_IN client, int _id = 0)
	{
		connSocket = socket;
		clientAddr = client;
		id = _id;
	}
}THREAD_DATA;

//线程函数
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	THREAD_DATA* pThreadData = static_cast<THREAD_DATA*>(lpParameter);
	SOCKET connSocket = pThreadData->connSocket;
	SOCKADDR_IN clientAddr = pThreadData->clientAddr;
	int id = pThreadData->id;

	cout << "[Client " << id << "] Connected.已连接.\n";

	SendCmd(connSocket);

	while (true)
	{
		if (!ProcessMsg(connSocket, id))break;
	}
	cout << "[Client " << id << "] Disconnected.断开连接.\n";
	tMSG_BYE p_bye;
	send(connSocket, reinterpret_cast<char*>(&p_bye), sizeof(p_bye), 0);
	closesocket(connSocket);
	return 0L;
}

int main(int argc, char** argv)
{
	int port = 1234;
	if (argc > 1)
	{
		port = atoi(argv[1]);
		if (port < 1024 || port>65534)
		{
			printf("Port error. Use %s port\n", argv[0]);
		}
	}
	//加载套接字库
	WSADATA wsaData;
	int iRet = 0;
	iRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iRet != 0)
	{
		cout << "WSAStartup(MAKEWORD(2, 2), &wsaData) execute failed!" << endl;;
		return -1;
	}
	if (2 != LOBYTE(wsaData.wVersion) || 2 != HIBYTE(wsaData.wVersion))
	{
		WSACleanup();
		cout << "WSADATA version is not correct!" << endl;
		return -1;
	}

	//创建套接字
	SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);
	if (serverSocket == INVALID_SOCKET)
	{
		cout << "serverSocket = socket(AF_INET, SOCK_STREAM, 0) execute failed!" << endl;
		return -1;
	}

	//初始化服务器地址族变量
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(port);
	//绑定
	iRet = bind(serverSocket, reinterpret_cast<SOCKADDR*>(&addrSrv), sizeof(SOCKADDR));
	if (iRet == SOCKET_ERROR)
	{
		cout << "bind(serverSocket, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) execute failed!" << endl;
		return -1;
	}

	//监听
	iRet = listen(serverSocket, CONNECT_NUM_MAX);
	if (iRet == SOCKET_ERROR)
	{
		cout << "listen(serverSocket," << CONNECT_NUM_MAX << ") execute failed!" << endl;
		return -1;
	}

	//等待连接_接收_发送
	SOCKADDR_IN clientAddr;
	int len = sizeof(SOCKADDR);
	int count = 0;
	cout << "Server is started\n";
	while (1)
	{
		SOCKET connSocket = accept(serverSocket, reinterpret_cast<SOCKADDR*>(&clientAddr), &len);
		if (connSocket == INVALID_SOCKET)
		{
			cout << "accept(serverSocket, (SOCKADDR*)&clientAddr, &len) execute failed!" << endl;
			return -1;
		}

		THREAD_DATA threadData1(connSocket, clientAddr, count++);
		HANDLE thread = CreateThread(nullptr, 0, ThreadProc, &threadData1, 0, nullptr);
		CloseHandle(thread);
		//		//C++ 中 map 容器的说明和使用技巧
		//		//http://www.cnblogs.com/anywei/archive/2011/10/27/2226830.html
		//		g_Map->insert[threadData1.id] = thread;
	}
	return 0;
}


  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
#include <iostream>
#include <fstream>
#include <string>
#include <winsock2.h>
#include "../FTPServer/header.h"
using namespace std;
#define BYE "BYE"
#pragma comment(lib, "ws2_32.lib")

void ReplayCMD(SOCKET socket, tMSG_HEADER *p_msg_header)
{
	string sClientCommad;
	tMSG_CMD *p_cmd = static_cast<tMSG_CMD*>(p_msg_header);
	cout << p_cmd->commad;
	cin >> sClientCommad;
	if (strcmp(sClientCommad.c_str(), "/q") == 0)
	{
		//cout << "客户端输入的是/q\n";
		/*断开连接*/
		tMSG_BYE p_bye;
		send(socket, reinterpret_cast<char*>(&p_bye), sizeof(p_bye), 0);
	}
	else
	{
		//cout << "客户端输入的是文件名:" << sClientCommad << endl;
		/*发送文件名*/
		tMSG_FILENAME p_filename;
		strcpy(p_filename.szFileName, sClientCommad.c_str());
		send(socket, reinterpret_cast<char*>(&p_filename), sizeof(p_filename), 0);
	}
}

void SayHello(SOCKET socket)
{
	tMSG_HELLO hello;
	send(socket, reinterpret_cast<char*>(&hello), sizeof(hello), 0);
}

void ReadyToRecvFile(SOCKET socket, tMSG_HEADER *p_msg_header)
{
	tMSG_FILE_LENGTH *p_file_length = static_cast<tMSG_FILE_LENGTH*>(p_msg_header);
	cout << "[" << p_file_length->szFileName << "] Length=" << p_file_length->lLength << " ";
	/*若文件已存在则先删除trunc*/
	ofstream fout(p_file_length->szFileName, ios::out | ios::trunc);
	if (!fout) { cout << "[" << p_file_length->szFileName << "] Open File Error!\n"; }
	fout.close();
	cout << "Ready to recv file... 准备接收文件...\n";
	tMSG_CLIENT_READY ready(0L, p_file_length->lLength);
	strcpy(ready.szFileName, p_file_length->szFileName);
	send(socket, reinterpret_cast<char*>(&ready), sizeof(ready), 0);
}

void RecvFile(SOCKET socket, tMSG_HEADER *p_msg_header)
{
	tMSG_FILE *p_msg_file = static_cast<tMSG_FILE*>(p_msg_header);
	long start = p_msg_file->tFile.lStart;
	long sum = p_msg_file->tFile.lFileLength;
	long size = p_msg_file->tFile.lSize;
	cout << "[" << p_msg_file->tFile.szFileName << "] Recveived: " << start + size << "/" << sum << ", ";
	ofstream fout(p_msg_file->tFile.szFileName, ios::out | ios::binary | ios::app);
	fout.write(p_msg_file->tFile.szBuff, p_msg_file->tFile.lSize);
	cout << "File Written.已写入 ";
	if (start + size < sum)//没有接收完
	{
		cout << "准备接受余下内容\n";
		tMSG_CLIENT_READY ready(start + size, sum);
		strcpy(ready.szFileName, p_msg_file->tFile.szFileName);
		send(socket, reinterpret_cast<char*>(&ready), sizeof(ready), 0);
	}
	else//接收完毕
	{
		cout << "All Recveived !已接收\n";
		tMSG_SEND_FILE_SUCC succ;
		strcpy(succ.szFileName, p_msg_file->tFile.szFileName);
		send(socket, reinterpret_cast<char*>(&succ), sizeof(succ), 0);
	}
	fout.close();
}

int main(int argc, char** argv)
{
	string host = "127.0.0.1";
	int port = 1234;
	if (argc == 3)
	{
		host = argv[1];
		port = atoi(argv[2]);
		if (port < 1024 || port>65534)
		{
			printf("Port error.Use %s host port\n", argv[0]);
		}
	}
	else if (argc != 1)
	{
		printf("Error. Use %s host port\n", argv[0]);
	}
	//加载套接字库
	WSADATA wsaData;
	int iRet = 0;
	iRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iRet != 0)
	{
		cout << "WSAStartup(MAKEWORD(2, 2), &wsaData) execute failed!" << endl;
		return -1;
	}
	if (2 != LOBYTE(wsaData.wVersion) || 2 != HIBYTE(wsaData.wVersion))
	{
		WSACleanup();
		cout << "WSADATA version is not correct!" << endl;
		return -1;
	}

	//创建套接字
	SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0);
	if (clientSocket == INVALID_SOCKET)
	{
		cout << "clientSocket = socket(AF_INET, SOCK_STREAM, 0) execute failed!" << endl;
		return -1;
	}

	//初始化服务器端地址族变量
	SOCKADDR_IN srvAddr;
	srvAddr.sin_addr.S_un.S_addr = inet_addr(host.c_str());
	srvAddr.sin_family = AF_INET;
	srvAddr.sin_port = htons(port);

	//连接服务器
	iRet = connect(clientSocket, (SOCKADDR*)&srvAddr, sizeof(SOCKADDR));
	if (0 != iRet)
	{
		cout << "connect(clientSocket, (SOCKADDR*)&srvAddr, sizeof(SOCKADDR)) execute failed!" << endl;
		return -1;
	}

	char szBuff[MAX_PACKET_SIZE + 1];
	while (true)
	{
		memset(szBuff, 0, sizeof(szBuff));
		//接收消息
		int nRecv = recv(clientSocket, szBuff, MAX_PACKET_SIZE, 0);
		//		cout << "接收数据长度=" << nRecv << endl;
		if (nRecv > 0)
		{
			szBuff[nRecv] = '\0';
		}
		else
		{
			closesocket(clientSocket);
			WSACleanup();
			return 0;
		}
		//解析
		tMSG_HEADER *p_msg_header = reinterpret_cast<tMSG_HEADER*>(szBuff);
		//		cout << (int)p_msg_header->cMsgId;
		switch (p_msg_header->cMsgId)
		{
		case MSG_CMD:/*服务器命令*/
			ReplayCMD(clientSocket, p_msg_header);
			break;
		case MSG_OPENFILE_ERROR:/*文件不存在*/
			cout << "File Not Found!服务器上无此文件\n";
			SayHello(clientSocket);
			break;
		case MSG_FILELENGTH:/*文件存在,客户端准备接收*/
			ReadyToRecvFile(clientSocket, p_msg_header);
			break;
		case MSG_FILE:/*接收文件*/
			RecvFile(clientSocket, p_msg_header);
			break;
		case MSG_BYE:
			closesocket(clientSocket);
			WSACleanup();
			return 0;
		default:
			cout << "Invalid data.\n";
			//SayHello(clientSocket);

			break;
		}
		//		Sleep(500);
	}
}

提示一

双重加密,或交换公钥。

解法1

a锁上给b,b再把他的锁锁上给a,a拿到把自己的锁解下,再传给b

解法2

可以先让b把打开的锁传给a(公钥),然后a使用盒子装好目标文件后加上b的锁(使用公钥加密),继而传给b,最后b解锁(私钥))

————其实都是QQ好友的评论2333
代码可在github上找到:https://github.com/YouthLin/Network

Reader Echoes

4 comments

  • #1386mothyrose
    『智力题』AB各有一把钥匙和锁,现在A要用加锁的盒子通过快递C传一个东西给B,但是如果C能打开盒子的话就会私吞这个东西,问AB该怎么做才能确保东西能从A传给B。 说一下我个人关于这个题目的解答: 物品:一个可以上锁的盒子,锁A,锁B,钥匙A,钥匙B。 分配:(假设A,B之间的传输从开始) A:把盒子,锁A,锁B,钥匙A给A。 B: 让B持有钥匙B。 传输过程:A把锁A放进盒子里,用锁B给盒子上锁,通过C传递给B,B拿到盒子以后用钥匙B打开盒子,取出东西后,再把自己想回复给A的东西和锁B一起放进盒子里,用锁A给盒子上锁,再通过C传回给A。即可完成一次传输,最终状态与最初相同,可以重复完成上述过程反复进行传输。
  • #1385cxf
    自己看过java的socket编程,很浅,这次看了c++的,c++的感觉更深入些
  • #1383咸菜一点米
    没有说上锁传东西的答案。。

表情

评论提交后需经审核才会显示。