1、需求说明
此项目主要为了实现客户端与服务器端文件的上传与下载功能,故开发时需要完成客户端程序和服务器端程序。
2、项目开发框架及工程结构
服务器端程序整体工程结构如下
客户端程序整体如下
其中并发服务器模型(一对多)的代码框架如下
while(1){
cli_sockfd=accept(ser_sockfd, (SA *)&cli_addr, &cli_len);
if(cli_sockfd<0){
perror("accept");
}
pthread_create(&tid, NULL, recv_mess, &cli_sockfd);
}
无连接时accept 函数会阻塞。每当产生一个新连接便创建一个新线程并传入其对应的新套接字描述符来响应每一个客户端。
客户端程序采用轮询的方式调用每个功能函数,每个功能实现与服务器的短连接(例如查询服务器目录信息功能在其调用结束时关闭连接)
while(1){
printf("ftp>");
bzero(cmd,N);
if(fgets(cmd,N-1,stdin)==NULL){
perror("fgets");
return -1;
}
cmd[N-1]='\0';
printf("input command is %s\n",cmd);
if(strncmp(cmd, "help", 4)==0){
help_cmd();
}else if (strncmp(cmd, "quit", 4)==0){
printf("感谢使用!再见\n");
break;
}else if((strncmp(cmd, "ls", 2)==0) && strlen(cmd) == 3){
ls_cmd(addr,cmd);
}else if(strncmp(cmd, "get", 3)==0){
get_cmd(addr, cmd);
}else if(strncmp(cmd, "put", 3)==0){
put_cmd(addr, cmd);
}else if(strncmp(cmd, "ls cli", 6)==0){
do_cli_ls_l();
}
else {
printf("command is error,please try again!\n");
}
}
查询服务器文件信息功能如下(其结果等待服务器端程序返回)
void ls_cmd(struct sockaddr_in addr, char *cmd)
{
int sockfd;
int ret;
//system("clear");
sockfd=socket(AF_INET, SOCK_STREAM, 0);
if(sockfd<0){
perror("socket");
exit(1);
}
//int on=1;
//setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
ret=connect(sockfd, (SA *)&addr, sizeof(addr));
if(ret<0){
perror("connect");
exit(1);
}
ret = write(sockfd, cmd, N);
if(ret<0){
perror("write");
exit(1);
}
printf(RED "************************************\n");
printf( "** 服务器文件内容 **\n");
printf( "************************************\n" NONE);
bzero(cmd, N);
while(read(sockfd, cmd, N) > 0){
printf(LIGHT BLUE "****** %s\n" NONE ,cmd);
bzero(cmd, N);
}
close(sockfd);
}
3、TCP通信中的应答机制确保数据的可靠性
如下是等待服务器端程序发送是否准备就绪应答,若就绪则客户端开始接收,否则return结束。
if (write(sockfd, cmd, strlen(cmd)) < 0) { //发送命令给服务器端 put xxx
printf("put cmd error 1\n");
exit(1);
}
bzero(buffer, N);
read(sockfd, buffer, N);//读取服务器端应答 "ok" ,阻塞在此
//printf("strncmp=%d\n", strncmp(buffer, "ok", 2));
if(!strncmp(buffer, "ok", 2) == 0){
printf("server error\n");
return ;
}
bzero(filename, N);
strncpy(filename, cmd+4, strlen(cmd+4) - 1); //取出文件名 xxx,-1是去掉\n
printf("filename is %s\n", filename);
fd=open(filename, O_RDONLY); // put filename ==> filename
if (fd < 0) {
perror("open");
write(sockfd, "error\n", N);
return;
}
write(sockfd, "success\n", N); //必须发此句,防止服务端read函数阻塞导致异常!!!!
bzero(buffer, N);
read(sockfd, buffer, N);//读取服务器端应答 "ok"
printf("strncmp=%d\n", strncmp(buffer, "ok",2));
if(!strncmp(buffer, "ok",2) == 0) {
printf("server error\n");
return;
}
当传输的文件较大时,或服务器端与客户端程序运行环境存在差异时方能体现这种应答机制的重要性。