流程:
客户端给服务器发送要下载的文件名
服务器收到文件名之后,判断当前路径下有没有该文件
//open(O_RDONLY) 如果报错 且错误码为 ENOENT 说明文件不存在
如果不存在,服务器给客户端发送文件不存在的消息
如果存在,服务器也要先发送文件存在的消息给客户端
然后循环读取文件内存,发送给客户端
客户端接到服务器的应答后,
如果文件不存在,则重新发送文件名给服务器
如果存在,open(O_WRONLY|O_CREAT|O_TRUNC,0664)
循环读取文件内容并写入文件
注意:服务器程序和客户端程序不要运行在同一个路径下。
运行结果图:
服务器端
客户端
代码如下:
service.c (head.h为自定义的头文件,在文末有)
#include <head.h>
typedef struct _MSG
{
int len;
char file_buff[128];
} msg_t;
int main(int argc, const char *argv[])
{
if (3 != argc)
{
printf("usage error:./a.out <ip> <port>...\n");
exit(-1);
}
// 创建套接字
int sockfd;
msg_t msg;
if (-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
{
PRINT_ERR("socket error");
}
// 填充服务器网络信息结构体
struct sockaddr_in serveraddr;
socklen_t serveraddr_len = sizeof(serveraddr);
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
// 绑定
if (-1 == bind(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
{
PRINT_ERR("bind error");
}
// 监听
if (-1 == listen(sockfd, 3))
{
PRINT_ERR("listen error");
}
int acceptfd=0;
int fd;
int nbytes;
char file_name[32] = {0};
char buff[128] = {0};
struct sockaddr_in clientaddr;
socklen_t clientaddr_len = sizeof(clientaddr);
while (1)
{
memset(file_name, 0, sizeof(file_name));
printf("正在等待客户端连接...\n");
// 等待连接
if (-1 == (acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &clientaddr_len)))
{
PRINT_ERR("accept error");
}
// 收发数据
printf("客户端[%s:%d]连接成功...\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
// while (1)
// {
RENAME:
if (-1 == (nbytes = recv(acceptfd, file_name, sizeof(file_name), 0)))
{
PRINT_ERR("recv001 error");
}
else if (nbytes == 0)
{
printf("客户端[%s:%d]断开了连接...\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
continue;
}
printf("客户端[%s:%d]要下载的文件为[%s]...\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port), file_name);
if (-1 == (fd = open(file_name, O_RDONLY)))
{
if (errno == ENOENT)
{
printf("客户端[%s:%d]要下载的文件[%s]不存在...\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port), file_name);
strcpy(buff, "file_no_exit");
if (-1 == send(acceptfd, buff, sizeof(buff), 0))
{
PRINT_ERR("send error");
}
goto RENAME;
}
else
{
PRINT_ERR("open error");
}
}
else
{
strcpy(buff, "file_exit");
if (-1 == send(acceptfd, buff, sizeof(buff), 0))
{
PRINT_ERR("send error");
}
memset(&msg, 0, sizeof(msg));
nbytes=0;
while (0 < (nbytes = read(fd, msg.file_buff, sizeof(msg.file_buff))))
{
msg.len = nbytes;
if (-1 == send(acceptfd, &msg, sizeof(msg), 0))
{
PRINT_ERR("send error");
}
memset(&msg, 0, sizeof(msg));
// }
}
msg.len = 0;
if (-1 == send(acceptfd, &msg, sizeof(msg), 0))
{
PRINT_ERR("send error");
}
}
close(fd);
close(acceptfd);
}
// }
close(sockfd);
return 0;
}
client.c
#include <head.h>
typedef struct _MSG
{
int len;
char file_buff[128];
} msg_t;
int main(int argc, const char *argv[])
{
// 入参合理性检查
if (3 != argc)
{
printf("usage error:%s <ip> <port>", argv[0]);
exit(-1);
}
// 创建套接字
int sockfd;
msg_t msg;
char file_name[32] = {0};
char buff[128] = {0};
int nbytes = 0;
int fd;
if (-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
{
PRINT_ERR("socket error");
}
// 填充服务器网络信息结构体
struct sockaddr_in serveraddr;
socklen_t serveraddr_len = sizeof(serveraddr);
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
//
if (-1 == connect(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
{
PRINT_ERR("connect error");
}
printf("与服务器连接成功...\n");
while (1)
{
RENAME:
printf("请输入您要下载的文件名称(quit 退出):");
fgets(file_name, sizeof(file_name), stdin);
file_name[strlen(file_name) - 1] = '\0';
if (strcmp(file_name, "quit") == 0)
{
printf("您主动断开了连接,程序已退出...\n");
break;
}
if (-1 == send(sockfd, file_name, sizeof(file_name), 0))
{
PRINT_ERR("send error");
}
if (-1 == recv(sockfd, buff, sizeof(buff), 0))
{
PRINT_ERR("recv error");
}
if (strcmp(buff, "file_no_exit") == 0)
{
printf("您要下载的文件[%s]不存在...\n", file_name);
goto RENAME;
// break;
}
else if ((strcmp(buff, "file_exit") == 0))
{
printf("文件[%s]存在,开始下载...\n", file_name);
if (-1 == (fd = open(file_name, O_WRONLY | O_CREAT | O_TRUNC, 0664)))
{
PRINT_ERR("open error");
}
while (1)
{
memset(&msg, 0, sizeof(msg));
if (-1 == (nbytes = recv(sockfd, &msg, sizeof(msg), 0)))
{
PRINT_ERR("recv error");
}
if (msg.len == 0)
{
break;
}
write(fd, msg.file_buff, msg.len);
}
printf("文件[%s]下载完成,程序退出...\n", file_name);
break;
close(fd);
}
}
close(sockfd);
return 0;
}
head.h(只使用需要的头文件即可,下面包含的有其他项目的头文件)
#ifndef __HEAD_H__
#define __HEAD_H__
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <dirent.h> //opendir closedir readdir函数头文件
#include <sys/wait.h>
#include <pthread.h> //创建线程
#include <semaphore.h> //无名信号量
#include <signal.h>
#include <sys/ipc.h> //IPC进程间通信键的获取
#include <sys/msg.h> //IPC消息队列
#include <sys/shm.h> //IPC共享内存
#include <sys/sem.h> //IPC信号灯集
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h> // inet_addr 将点分十进制的字符串 转换成 网络字节序的 无符号四字节整型
#define PRINT_ERR(errmsg) \
do \
{ \
perror(errmsg); \
return -1; \
} while (0)
#endif