Server:
fileserver.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "rw.h"
#define BACKLOG 10
void *deal_request(void* arg)
{
int connfd = *((int *)arg);
char file_name[MAX_LEN];
int res =0;
pthread_detach(pthread_self());//当线程终止运行后自动清理所占资源并退出
res = read_cmd(connfd,file_name,sizeof(file_name));
printf("res = %d",res);
if(res == -1)
printf("read file name error\n");
else{
//printf("read_cmd :%s",file_name);
printf("jjres = %d",res);
if(send_file(connfd,file_name)==-1){
printf("Send file error\n");
}
}
//printf("res = %d",res);
/* if(res = read_cmd(connfd,file_name,sizeof(file_name))==-1){
printf("read file name error\n");
}
else{
//printf("read_cmd :%s",file_name);
printf("jjres = %d",res);
if(send_file(connfd,file_name)==-1){
printf("Send file error\n");
}
}*/
printf("deal_request finish\n");
free(arg);//释放动态分配的内存
close(connfd);
pthread_exit(NULL);
}
int main(int argc, char** argv)
{
int sockfd;
int* connfd;
struct sockaddr_in servaddr;
struct sockaddr_in cliaddr;
struct sockaddr_in tempaddr;
socklen_t templen;
socklen_t clilen;
if((sockfd = socket(AF_INET, SOCK_STREAM, 0))==-1){
perror("socket");
exit(1);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = 0;//自动分配端口
if(bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))==-1){
perror("bind error");
exit(1);
}
templen = sizeof(struct sockaddr);
//由于端口号自动分配 这里获取套接字本地地址以获得端口号
if(getsockname(sockfd,(struct sockaddr*)&tempaddr,&templen)==-1){
perror("get sock name error");
exit(1);
}
printf("Server is listening on port %d\n",ntohs(tempaddr.sin_port));
if(listen(sockfd, BACKLOG) == -1){
perror("listen");
exit(1);
}
while(1)
{
pthread_t pthread;
clilen = sizeof(cliaddr);
connfd = (int *)malloc(sizeof(int));//多线程中 套接字一定要动态分配 否则不同的线程都指向同一个connfd
*connfd = accept(sockfd,(struct sockaddr *)&cliaddr, &clilen);
if(*connfd == -1){
perror("accept");
continue;
}
if(pthread_create(&pthread,NULL,deal_request,connfd)!=0){
perror("pthread_create");
break;
}
}
return 0;
}
rw.h
#ifndef __RW_H
#define __RW_H
#define MAX_LEN 1024*10 //读写缓冲大小
ssize_t readall(int fd, void* buf, size_t* len);
ssize_t writeall(int fd, void* buf, size_t* len);
int read_cmd(int sockfd, char* cmd_buf, int len);
int send_cmd(int sockfd, char* file_name);
#endif
rw.c
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include "rw.h"
//read函数扩展,解决大数据时,read分次读的问题
ssize_t readall(int fd, void* buf, size_t* len)
{
size_t nleft;
ssize_t nread;
ssize_t total;
char* ptr;
ptr = buf;
nleft = *len;
total = 0;
while(nleft>0)//反复读,直到无数据可读
{
if((nread = read(fd,ptr,*len))==-1){
perror("readall");
break;
}
if(nread ==0){
break;
}
nleft -=nread;
ptr +=nread;
total +=nread;
*len = nleft;
}
*len = total;
return(nread == -1)?-1:0;
}
//write函数扩展,解决大数据时,write分次写的问题
ssize_t writeall(int fd, void* buf, size_t* len)
{
size_t nleft;
ssize_t nwrite;
ssize_t total;
const char* ptr;
ptr = buf;
nleft= *len;
total = 0;
while(nleft>0)//反复写,直到数据都写入
{
if((nwrite = write(fd, ptr, *len)) == -1){
perror("write all");
break;
}
nleft -= nwrite;
ptr += nwrite;
total += nwrite;
*len = nleft;
}
*len = total;
return (nwrite == -1)?-1:0;
}
//分析客户端命令,提出取请求的文件名
int read_cmd(int sockfd, char* cmd_buf, int len)
{
char line[MAX_LEN];
int my_len = 0;
int total_len =0;
char *ptr;
int can_read;
if(len>MAX_LEN)
len = MAX_LEN;
can_read = 1;
strcpy(cmd_buf,"\0");
while(can_read)
{
if((my_len = read(sockfd,line,len))<0){
perror("read");
return -1;
}
total_len = total_len + my_len;
if(total_len > len){
printf("Recieve command error!\n");
return -1;
}
//strstr(char *str1, char *str2):从字符串str1中查找是否有符串str2,如果有,从str1中的str2位置起,返回str1的指针,如果没有,返回null。
printf("line =%s",line);
if((ptr = strstr(line,"\r\n"))==NULL){
if(total_len<=len){
//char *strcat(char *dest,char *src):把src所指字符串添加到dest结尾处(覆盖dest结尾处的'\0')并添加'\0'。
strcat(cmd_buf,line);
}
}
else{
//char *strncat(char *dest,char *src,int n):把src所指字符串的前n个字符添加到dest结尾处(覆盖dest结尾处的'\0')并添加'\0'。
strncat(cmd_buf, line, ptr-line);
can_read =0;
}
printf("Client requests file:%s\n",cmd_buf);
}
return 0;
}
//发文件至客户端
int send_file(int sockfd, char* file_name)
{
int file_fd;
int file_size;
int read_left;
int len;
int error_flag;
int readlen;
struct stat file_state;
char buffer[MAX_LEN];
int dot_number = 0;
if((file_fd = open(file_name,O_RDONLY)) == -1){
perror("open");
return -1;
}
//int fstat(int fildes,struct stat *buf):将参数fildes所指的文件状态,复制到buf所指的结构中。
if(fstat(file_fd,&file_state)==-1){
perror("fstat");
return -1;
}
file_size = file_state.st_size;
read_left = file_size;
len = MAX_LEN;
while(read_left>0)
{
readlen = MAX_LEN;
error_flag = readall(file_fd,buffer,&readlen);
if(error_flag<0){
return -1;
}
read_left -=readlen;
len =readlen;
error_flag = writeall(sockfd,buffer,&len);
if(error_flag == -1){
return -1;
}
if(readlen ==0&&read_left!=0){
printf("the file is not read fully\n");
return -1;
}
if(read_left ==0){
printf("\nServer sent file over!\n");
}
}
close(file_fd);
return 0;
}
Client:
fileclient.c
#include <strings.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "rw.h"
#define DOT_PERIOD 50
int main(int argc, char** argv)
{
int sockfd;
int conn_ret;
struct sockaddr_in servaddr;
char cmd_buf[MAX_LEN];
char recvbuf[MAX_LEN];
int error_flag;
int len = MAX_LEN;
int file_fd;
int dot_number;
int total_len = 0;
if(argc!=5){
printf("Usage: fileclient <address> <port> <src file> <des file>\n");
return 0;
}
if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1){
perror("sock");
exit(1);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(atoi(argv[2]));
inet_pton(AF_INET,argv[1],&servaddr.sin_addr);
conn_ret = connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
if(conn_ret = -1){
perror("connect");
}
//创建接收文件
file_fd = open(argv[4],O_CREAT | O_WRONLY);
if(file_fd == -1){
perror("open");
exit(1);
}
//构建客户端的请求消息 格式为“文件名\r\n”
len = strlen(argv[3])+3;
snprintf(cmd_buf,len,"%s\r\n",argv[3]);
printf("cmd_buf %s\n",cmd_buf);
if((error_flag = writeall(sockfd,cmd_buf,&len))==-1){
printf("writeall error!\n");
exit(1);
}
//缓冲大小,决定每次I/O操作的数据量
len = MAX_LEN;
printf("\nfile is transferring:\n");
while((error_flag = readall(sockfd,recvbuf,&len))==0)
{
if(len ==0){
printf("\nClient has recieved file!\n");
break;
}
printf(".");
printf("read length is %d\n",len);
dot_number++;
if((dot_number%DOT_PERIOD)==0)
{
printf("\n");
dot_number = 0;
}
total_len +=len;
//将接收到数据写入文件
if(writeall(file_fd,recvbuf,&len) == -1)
{
printf("\nclient has some error when receive the file \n");
break;
}
len = MAX_LEN;
}
printf("\nRecevied %d bytes\n",total_len);
close(file_fd);
close(sockfd);
return 0;
}
Makefile:
CC=gcc
all:fileserver fileclient
rw.o:rw.c
$(CC) -c rw.c -o rw.o
fileserver:fileserver.c rw.o
$(CC) fileserver.c rw.o -o fileserver -lpthread
fileclient:fileclient.c rw.o
$(CC) fileclient.c rw.o -o fileclient
clean:
@rm fileserver rw.o fileclient