服务器实现功能:利用网络编程实现上传下载文件,显示服务器和客户端所在路径所有文件;利用多线程实现并发服务器。
思路:1.服务器首先要设置好IP,端口等网络编程要使用到的信息;然后通过while循环实现循环接受客户端的连接,并且连接后创建线程,实现并发跳转到线程处理函数;其次在线程处理函数里面使用switch语句来判断客户端的请求;最后判断客户端的请求以后调用相应的函数进行处理。
2.客户端首先也是要设置IP,端口等信息,然后通过while循环显示可用功能并且循环处理客户请求;然后在while循环里面使用switch语句来判断客户的请求。最后调用相应的函数进行处理。
服务器代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
void *thread_fun(void *);//线程处理函数
void list(long);//显示服务器所在路径的文件函数
void download(long);//下载文件函数
void upload(long);//上传文件函数
int main(int argc, char* argv[])
{
int sockfd=socket(AF_INET,SOCK_STREAM,0),connfd;//创建套接字,使用IPV4地址通信并且使用流式套接字
if(sockfd==-1)
{
perror("socket");
return -1;
}
struct sockaddr_in ser,cli;//该结构体存储地址等信息
ser.sin_family=AF_INET;//设置IPV4通信
ser.sin_port=htons(8888);//设置端口号
ser.sin_addr.s_addr=inet_addr("0.0.0.0");//会自动匹配可用IP,也可换成自己电脑的IP
socklen_t ser_len=sizeof(ser);//ser的大小
if(-1==bind(sockfd,(struct sockaddr*)&ser,ser_len))//绑定套接字
{
perror("bind");
close(sockfd);
return -1;
}
if(-1==listen(sockfd,10))//监听是否有客户端连接
{
perror("listen");
close(sockfd);
return -1;
}
socklen_t cli_len=sizeof(cli);//cli的大小,用来存储连接客户端的信息
char s[100];
while(1)
{
connfd=accept(sockfd,(struct sockaddr*)&cli,&cli_len);//接受客户端的连接
pthread_t tid;//线程ID
if(0!=pthread_create(&tid,NULL,thread_fun,(void *)(long)connfd))//创建线程,调用线程处理函数并且给线程处理函数传擦(通信套接字)
{
perror("pthread_create");
close(sockfd);
return -1;
}
pthread_detach(tid);//回收线程资源
}
close(connfd);
close(sockfd);
return 0;
}
void *thread_fun(void *arg)
{
char buf[20];
long connfd=(long)arg,flag=1;
while(flag)//循环读取客户端的请求,并且调用相应的函数进行处理
{
read(connfd,buf,sizeof(buf));
switch(buf[0])
{
case 'q':close(connfd);
pthread_exit(0);
flag=0;
break;
case 'l':list(connfd);
break;
case 'd':list(connfd);
download(connfd);
break;
case 'u':upload(connfd);
break;
}
}
}
void list(long connfd)
{
char buf[128];
DIR *fp=opendir(".");//打开当前目录
struct dirent* file;
while(file=readdir(fp))//读取当前目录下的文件并且将文件的参数传递给file存储,然后会自动读取下一个文件,如果该目录下文件读取完则返回NULL
{
strcpy(buf,file->d_name);//将文件的名字复制到buf中
write(connfd,buf,sizeof(buf));//通过通信套接字将文件名传递给客户端
}
write(connfd,"NULL",5);//读取完成后给客户端发送NULL代表读取完成
return;
}
void download(long connfd)
{
char buf[128];
long size;//存储文件的大小
int fd=-1;//文件描述符
read(connfd,buf,sizeof(buf));//接收客户端发送过来的文件名
fd=open(buf,O_RDONLY);//open打开文件用于后续的操作
FILE *fp=fopen(buf,"r");//fopen打开文件用于计算文件大小
fseek(fp,0,SEEK_END);
size=ftell(fp);
sprintf(buf,"%ld",size);//利用sprintf将文件大小转换成字符串传递给客户端,用于判断文件是否传输完成,也可用标志位完成
write(connfd,buf,sizeof(buf));
fclose(fp);
int ret=0;
while(ret=read(fd,buf,sizeof(buf)))//读取文件内容,若读取完成则返回0
{
write(connfd,buf,ret);//将读取的内容传递给客户端
}
close(fd);
return;
}
void upload(long connfd)
{
char buf[128];
read(connfd,buf,sizeof(buf));//接收客户端需要上传的文件名字
int fd=open(buf,O_WRONLY | O_CREAT,400);//以只写方式打开需要上传的文件,若没有则创建且赋予创建的文件读写的权限
read(connfd,buf,sizeof(buf));//接收文件的大小
int size=atoi(buf),ret=0;//将文件的大小转为整型数字
while(1)
{
ret+=read(connfd,buf,sizeof(buf));//ret为读取到传递过来的数据大小
write(fd,buf,ret);
if(ret==size)//若读取到的数据大小与文件大小一致,则代表读取完成跳出循环
break;
}
close(fd);
return;
}
客户端代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
void list(int);
void download(int);
void upload(int);
int main(int argc, char* argv[])
{
int sockfd=socket(AF_INET,SOCK_STREAM,0),connfd;
if(sockfd==-1)
{
perror("socket");
return -1;
}
struct sockaddr_in ser;
ser.sin_family=AF_INET;
ser.sin_port=htons(8888);
ser.sin_addr.s_addr=inet_addr("192.168.44.128");//设置服务器的可用IP
socklen_t len=sizeof(ser);
if(connect(sockfd,(struct sockaddr*)&ser,len))//主动请求连接服务器
{
perror("connect");
close(sockfd);
return -1;
}
char str[10];
int flag=1;
while(flag)//循环接收客户的请求并且发送给服务器
{
printf("*************请输入以下选项**************\n");
printf("l :显示目录下的文件 q :退出\n");
printf("d :下载文件 u :上传文件\n");
printf("*****************************************\n");
scanf("%s",str);
switch(str[0])
{
case 'q':write(sockfd,"q",2);
flag=0;
break;
case 'l':write(sockfd,"l",2);
list(sockfd);
break;
case 'd':write(sockfd,"d",2);
list(sockfd);
download(sockfd);break;
case 'u':write(sockfd,"u",2);
upload(sockfd);
break;
default :printf("输入错误,请重新输入\n");
break;
}
}
close(sockfd);
return 0;
}
void list(int sockfd)
{
char buf[128];
while(1)//循环读取从服务器发送过来的文件名称然后打印,直至读取到NULL代表读取完成
{
read(sockfd,buf,sizeof(buf));
if(strcmp(buf,"NULL")==0)
break;
printf("filename:%s\n",buf);
}
printf("以上是服务器路径所有文件\n\n");
return;
}
void download(int sockfd)
{
char buf[128];
printf("请输入需要下载的文件名\n");
scanf("%s",buf);
write(sockfd,buf,sizeof(buf));//将需要下载的文件名发送给服务器
int fd=open(buf,O_WRONLY | O_CREAT,400);
read(sockfd,buf,sizeof(buf));//接收文件的大小
int size=atoi(buf),ret=0;
while(1)
{
ret+=read(sockfd,buf,sizeof(buf));
write(fd,buf,ret);
if(ret==size)
break;
}
printf("下载完成\n\n");
close(fd);
return;
}
void upload(int sockfd)
{
DIR *f=opendir(".");
struct dirent* file;
while(file=readdir(f))
{
if(file->d_type=='d')
continue;
printf("filename:%s\n",file->d_name);
}
closedir(f);
char buf[128];
printf("请输入需要上传的文件名\n");
scanf("%s",buf);
write(sockfd,buf,sizeof(buf));
FILE *fp=fopen(buf,"r");
int fd=open(buf,O_RDONLY),ret=0;
fseek(fp,0,SEEK_END);
long size=ftell(fp);
sprintf(buf,"%ld",size);
write(sockfd,buf,sizeof(buf));
fclose(fp);
while(ret=read(fd,buf,sizeof(buf)))
{
write(sockfd,buf,ret);
}
printf("上传完成\n\n");
close(fd);
return;
}
结果截图
服务器和客户端在不同的路径,并且文件也有所区别
显示服务器所在路径的所有文件
下载服务器所在路径的文件
上传文件和退出
运行结果