客户端与服务器通信项目2

4、模块划分

4.1、服务器模块及功能
1、读取文件列表:获取服务器文件列表机文件详细信息
2、读取服务器配置:读取服务器名称与最大可连接数量
3、修改服务器配置:获取客户端发送的信息,并写入配置文件
4、开始传输文件:向客户端发送文件
5、暂停传输文件:暂停向客户端发送文件
6、停止传输文件:停止向客户端发送文件
7、多线程:同时响应多个客户端的连接,并处理响应的请求
4.2、客户端模块及功能
1、获取文件列表:接收服务器发送的文件列表信息,并打印
2、读取服务器配置:接收服务器发送的配置信息,并打印
3、修改服务器配置:输入配置信息,并发送给服务器
4、开始传输文件:从服务器下载文件
5、暂停传输文件:暂停从服务器下载文件
6、停止传输文件:停止从服务器下载文件
7、多线程:监听标准输入流,在文件传输过程中读取指令

5、结构体定义

5.1、描述客户端发送指令的结构体

typedef enum
{
	GET_FILE_LIST = 1,		//获取服务器文件列表
	GET_SER_CONFIG,			//获取服务器配置
	SET_SER_CONFIG,			//设置服务器配置
	FILE_TRANS_START,		//开始文件传输
	FILE_TRANS_PAUSE,		//暂停文件传输
	FILE_TRANS_STOP,		//停止文件传输
}cmd_type;

5.2、客户端与服务器之间的通信协议结构体

typedef struct protocol
{
	int length;				//整个文件的长度
	int cmd;				//对应数值的含义
	char name[NAME_LEN];	//只有当命令为0x02时,该字段才生效
}protocol;

5.3、描述服务器配置的结构体

typedef struct server_config
{
	char name[NAME_LEN];		//服务器名称
	int client_max;				//可连接最大客户端数量
}server_config;

5.4、描述传输文件信息的结构体

typedef struct file_info
{
	char name[NAME_LEN];		//传输文件名
	int length;					//传输文件大小
}file_info;

5.5、描述文件传输状态

typedef enum
{
	FILE_STATE_START = 1,	//开始传输
	FILE_STATE_PAUSE,		//暂停传输
	FILE_STATE_STOP,		//结束传输
}file_state;

6、获取服务器文件列表

6.1、静态库
在本项目中,将获取服务器文件列表的功能以静态库的形式进行封装。链接时,链接器将从文件中取得所需的代码,复制到生成的可执行文件中,与程序运行的似乎无关
使用静态链接库的特点是可执行文件中包含了库代码的完整拷贝,但当多次使用时就会出现多份冗杂拷贝
6.2、功能实现
通常,我们在Linux系统中,都是使用ls命令来获取当前目录文件列表。本项目中,需要实现客户端发送指令获取服务器目录的文件列表,即需要将命令ls的返回值存储在缓冲区,然后发送给客户端。
(1)opendir()输入并打开当前目录,返回存放目录信息的结构体。
(2)readdir()循环读取目录,依次返回指向目录子项的指针,每次返回一个,读取一个子项则目录指针后移一位,直到目录结尾或出错
(3)逐个读取目录子项的信息,存放在stat结构体中。
(4)按序将子项信息以一定的格式存入缓冲区中
(5)closedir()关闭当前目录
最后,服务器将存放文件列表信息的缓冲区发送给客户端,客户端收到进行打印

7、服务器配置

在本项目中,该功能分为两部分,一部分是获取服务器配置,一部分是修改服务器配置。主要通过对文件的读写进行操作。为了保证每次修改后的配置在重启后仍然生效,项目中将服务器配置信息,保存在服务器配置文件中,放在服务器目录下。
7.1、获取服务器配置
当客户端发送指令需要获取服务器配置时候,服务器接收指令并执行getconfig()函数,读取配置文件内容:

name = 132
client_max = 9

(1)使用fgets()函数读取一行字符串
(2)strstr()函数来定位“=”在字符串中的位置
(3)将“=”后面的内容以字符串或者数字的形式存放到结构体“server_config”中
最后服务器将“server_config”结构体中的内容发送给客户端,客户端接收到服务器的配置信息后,打印在终端
7.2、修改服务器配置
当客户端需要修改服务器配置,并发送待配置的信息时,服务器接收指令,并执行“setconfig”函数,使用sprintf()函数将接收到的配置信息格式化后存入到字符串中,并写入文件
注:在修改服务器配置前,需要验证待配置参数的有效性
(1)最大可连接客户端数量不能超过系统设定的值,本设计中不能超过10
(2)最大可连接客户端数不能小于当前已连接的客户端数

8、文件传输

8.1、文件传输开始
(1)客户端发送开始传输的命令和待传输的文件名给服务器。当上一次文件传输结束时,客户端会提示输入新的文件名。当上一次文件传输未完成时,默认传输上一次的文件。
(2)服务器接收到开始传输的命令和待传输的文件名。
(3)首先判断文件名是否有效,若本地无该文件,则文件名无效,清空文件信息结构体返回给客户端;若该文件存在,则计算待传输的文件大小返回给客户端。当重新传输新文件时,大小为完整的文件字节数。当继续传输原来的文件时,大小为上一次未传输的字节数。
(4)客户端与服务器相互确认后开始进行文件的收发。当接收新文件时,文件以附加的方式打开。当接收的为上一次未传输完的文件时,文件以只写方式打开。
(5)当数据发送、接收完成,则文件传输成功
8.2、文件传输暂停
当客户端与服务器之间传输文件时,客户端的主线程处于忙碌的状态。此时,子线程用于处理标准输入流。当接收到暂停命令时,文件状态file_state置为FILE_STATE_PAUSE。客户端保存文件信息,并发送暂停指令给服务器。服务器接收到指令后暂停文件传输
8.3、文件传输停止
当客户端与服务器之间传输文件时,客户端的主线程处于忙碌的状态。此时,子线程用于处理标准输入流。当接收到停止命令时,文件状态file_state置为FILE_STATE_STOP。客户端清空文件信息,并发送停止指令给服务器。服务器接收到指令后停止文件传输。

9、多线程

在本项目中,服务器开启多线程是为了响应不同客户端连接,每当有一个新的客户端建立连接,并且当前连接数小于最大可连接客户端数量,便开启一个线程,用于和客户端之间的交互。每个线程处理一个客户端与服务器之间的通信,互不干扰。当该客户端与服务器之间的连接中断,线程也就会关闭。客户端开启多线程是为了在文件传输过程中读取标准输入流,防止数据的频繁收发阻塞了指令的读取。
9.1、创建线程

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void*(start_rountine)(void*), void *arg);
1、thread:存放新线程的ID到该参数所指向的缓存区中
2、attr:线程的属性,默认为NULL
3、start_routine:指定新线程的处理函数,也就是新线程启动之后需要执行的代码,线程处理函数执行完毕之后,新线程终止
4、arg:用于给start_routine传递实参

创建多线程,将特定的函数指定为线程来运行
9.2、互斥量
互斥量本质上就是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁后,任何其他试图再次对互斥量加锁的线程都会被阻塞,直到当前线程释放该互斥锁。(初始化、锁定互斥、解锁互斥、销毁互斥)

pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;	//初始化
int pthread_mutex_lock(pthread_mutex_t *mutex);			//锁定互斥
int pthread_mutex_unlock(pthread_mutex_t *mutex);		//解锁互斥
int pthread_mutex_destroy(pthread_mutex_t *mutex);		//销毁互斥

9.3、SELECT读取标准输入
当客户端在进行文件传输时,主线程处于忙碌状态,这时子线程就采用了select()函数来实现非阻塞读取标准输入。
(1)使用结构体timeval设置超时时间
(2)FD_ZERO()清空待检查的文件描述符
(3)FD_SET()设置文件描述符关联标准输入
(4)select()在超时时间系统内系统不断监听套接口状态。此时用于接收文件传输暂停或终止命令。返回-1表示出错,返回0表示超时,返回正数表示发生状态变化的文件描述符的个数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值