Linux-网络通信-进阶

// FileDownloadThroughServer.cpp : Defines the entry point for the console application.
/*----------------------------------------------------------------------------------------------------------
功能:
    建立C/S结构的文件下载系统。每个客户端可以提供不定数目的文件用于共享,也可以下载别人共享出来的文件。
	文件存在于各个客户端上,而不在服务器上。
	要允许一个客户端同时下载多个文件,也要允许同时被多个别的客户端下载。
思路:
    1.服务器至少应该开放3个TCP端口供客户端连接。
	    (1)第一个用来接收客户端发来的共享文件名以及向客户端发送别人想要下载他的文件的通知,不妨把这个端口叫做通知端口。
		(2)第二个用来向客户端每隔10秒发送最新共享文件列表,把它叫做刷新端口。
		(3)第三个用来提供文件传输的中转服务,叫做传输端口。
	2.本系统是围绕着共享文件而工作的,为了方便起见,应该把一个共享文件的全部信息封装成“共享文件结构体”,成员变量
	  应该包括文件名、文件所在客户端的地址、服务器对应于这个客户端的通知套接字。
	3.通知端口和传输端口应该设计各自的长度固定的头部(可设计成结构体),叫做“通知头部”和“传输头部”。
	  其中“传输头部”又有下载和上传两种。这样这两个端口的recv()函数就可以指定固定的接收长度了。
	4.服务器和客户端都要大量使用多线程。
	5.服务器可以用c++stl的multimap来保存共享文件列表,multimap的第一个字段是服务器上面对应于文件所在客户端的通知套接字,
	  第二字段是共享文件结构体。这样设计一来是为了在客户端退出的时候能最方便地从multimap中删除这个客户端的所有共享文件信息,
	  二来是为了在用户指定要下载哪个文件时不用通过搜索就知道该向哪个客户端发送通知。由于这个数据结构在客户端登录和
	  退出时都要被修改,所以必须有并发控制。
	6.客户端的数据结构可以简单地用c++stl的vector来保存服务器发来的最新共享文件列表。vector中每个元素都是
	  共享文件结构体。为防止用户选择文件时正好在刷新共享文件列表,也需要有并发控制。但应知道,这里的并发控制可以
	  防止程序的运行出错,但并不能杜绝下载到非指定文件的可能性。
	7.系统的工作流程大致如下:服务器在3个端口上等待,客户端首先连接到通知端口,在这条连接上发送自己的共享文件名,
	  然后同样在这条连接上准备接收“通知头部”。接着客户端每隔10秒连接到刷新端口并接收最新共享文件列表。
	  客户端还需要用一个线程来准备读入用户的键盘输入,当用户有输入时,客户端连接到传输端口并发送一个表示下载的
	  “传输头部”(其中最重要的就是一个共享文件结构体),然后在这条连接上准备接收文件数据。服务器收到这个
	  “传输头部”后从中得到文件所在客户端的套接字,并构造一个“通知头部”(包括想下载文件的客户端的套接字和想下载的
	  文件名)发送到这个套接字上。被下载方在通知端口上收到“通知头部”后,也连接到传输端口,先发送一个表示上传
	  的“传输头部”(其中最重要的就是下载方的套接字),以便让服务器知道该把文件转发给谁,接着就可以开始上传文件了。
考查:
    1.结合源代码和注释,搞懂这个系统的结构和流程,掌握套接字编程和多线程编程的相关技术,准备回答问题。
    
	2.已知:getsockname()函数可以获得套接字所使用的地址。自行查阅这个函数的原型,并在源程序基础上添加代码,
	  使用户试图下载自己的文件时能阻止并提示“这个文件是你自己的”。
	  
	3.按照本源程序的方案,分析在大负荷的情况下系统会有什么表现?应怎么改进?动手改源程序或者准备回答问题。
	
	4.本源程序除了未进行差错处理和用户指定下载一个前10秒之内退出系统的客户端上的文件时会得不到正确提示
	  这些小问题外,故意留有一个真正的bug,此bug如果被激发,不仅会导致系统在一切正常并且被下载方没有退出时
	  也会下载不成功,而且服务器有不确定的动作。试进行debug。
----------------------------------------------------------------------------------------------------------*/
#include "stdafx.h"
#include "stdio.h"
#include <map>
#include <vector>
#include "Winsock2.h"
#pragma comment(lib, "ws2_32.lib")
//宏定义
//---------------------------------------------------------------------------------------------------------
#define SERVER_NOTIFY_PORT		1024	//通知端口
#define SERVER_REFRESH_PORT		1025	//刷新端口
#define SERVER_TRANSFER_PORT	1026	//传输端口
#define COMMAND_DOWNLOAD		0		//“传输头部”中表示下载的命令
#define COMMAND_UPLOAD			1		//“传输头部”中表示上传的命令
//---------------------------------------------------------------------------------------------------------
//结构体定义
//---------------------------------------------------------------------------------------------------------
#pragma pack(4)  //设置结构体按照4字节对齐
struct SHARED_FILE						//共享文件结构体,封装一个共享文件的所有信息。服务器端和客户端都要用到
{
	char				filename[100];	//文件名
	struct sockaddr_in	client_addr;	//文件所在的客户端的网络地址
	SOCKET				notify_sock;	//服务器上面对应于这个客户端的通知套接字
};
struct TRANSFER_HEADER					//“传输头部”,由客户端发往服务器,准备开始一次下载或上传
{
	int cmd;							//命令,指明本次传输是为了下载还是上传,0-下载,1-上传
	union  
	{
		SHARED_FILE	shared_file;		//如果是下载,需要告诉服务器想下载的共享文件结构体
		SOCKET		sock;				//如果是上传,需要告诉服务器想下载这个文件的客户端的套接字
	};
};
struct NOTIFY_HEADER					//“通知头部”,由服务器发往客户端,通知他有人想下载他的某个文件
{
	SOCKET	sock;						//告诉客户端是谁想下载他的文件
	char	filename[100];				//告诉客户端想下载他的哪个文件
};
#pragma pack()  //取消结构体的字节对齐
//---------------------------------------------------------------------------------------------------------

//全局变量
//---------------------------------------------------------------------------------------------------------
//服务器端的核心数据结构(多映射),用来保存所有共享文件的信息。映射的第一个字段是服务器上面对应于客户端的通知套接字,第二个字段是客户端的一个共享文件结构体
std::multimap<SOCKET, SHARED_FILE>		g_shared_files;			
//客户端的两个核心数据结构(数组),第一个用来暂存服务器发来的最新共享文件列表,第二个是第一个的拷贝
std::vector<SHARED_FILE>				g_files_list;
std::vector<SHARED_FILE>				g_files_list2;
unsigned int							g_serverip;				//服务器的IP,网络字节序表示	
HANDLE									g_server_semaphore;		//服务器使用的信号量,用于控制对g_shared_files的并发访问
HANDLE									g_client_semaphore;		//客户端使用的信号量,用于控制对g_files_list2的并发访问
//-------
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值