文件传输服务器
头文件
#ifndef __FUNCTION_H__
#define __FUNCTION_H__
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
//宏定义IP和端口号
#define IP "192.168.8.120"
#define PORT 5555
#define N 512
//宏函数包装一个带行号的错误提示
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__", __LINE__);\
perror(msg);\
}while(0)
void deal_cli_msg(int newfd, struct sockaddr_in cin);
int upload(int newfd, struct sockaddr_in cin, char* filename,int size);
int download(int newfd, struct sockaddr_in cin,char buf[]);
int listdir(int newfd, struct sockaddr_in cin);
typedef void (*sighandler_t)(int);
int socket_init(void);
void handler(int sig);
#endif
主函数
#include "./function.h"
int main(int argc, const char *argv[])
{
//捕获17号信号
sighandler_t s = signal(SIGCHLD, handler);
if(SIG_ERR == s)
{
ERR_MSG("signal");
return -1;
}
//创建流式套接字
int sfd = socket_init();
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
int newfd = 0;
pid_t pid = 0;
//循环接收连接请求
while(1)
{
newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);
if(newfd < 0)
{
ERR_MSG("accept");
return -1;
}
printf("[%s : %d]newfd = %d accept success\n",\
inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);
pid = fork();
if(pid > 0)
{
close(newfd);
}
else if(0 == pid)
{
close(sfd);
deal_cli_msg(newfd, cin);
close(newfd);
exit(0);
}
else
{
ERR_MSG("fork");
return -1;
}
}
close(sfd);
return 0;
}
功能函数
#include "./function.h"
//回收僵尸进程
void handler(int sig)
{
while(waitpid(-1, NULL, WNOHANG) > 0);
}
//获取套接字函数
int socket_init()
{
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("socket create success\n");
//设置端口快速重用
int reuse = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt");
return -1;
}
//填充服务器的IP地址以及端口号
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
//绑定服务器的地址信息结构体
if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0 )
{
ERR_MSG("bind");
return -1;
}
printf("bind success\n");
//将套接字设置为被动监听状态,让内核去监听是否有客户端连接
if(listen(sfd, 10) < 0)
{
ERR_MSG("listen");
return -1;
}
printf("listen success\n");
return sfd;
}
//展示目录函数
int listdir(int newfd, struct sockaddr_in cin)
{
//准备传输数据
char buf[256] = "";
ssize_t res = 0;
DIR* dir = opendir("./");
struct dirent* dirp;
while(dirp = readdir(dir))
{
if('.' != *dirp->d_name)
{
res = send(newfd, dirp->d_name, sizeof(dirp->d_name), 0);
if(res < 0)
{
ERR_MSG("send");
return -1;
}
}
}
bzero(buf,sizeof(buf));
if(send(newfd, buf, 1, 0) <0)
{
ERR_MSG("send");
return -1;
}
printf("目录发送完毕\n");
return 0;
}
//处理交互信息函数
void deal_cli_msg(int newfd, struct sockaddr_in cin)
{
char buf[N] = "";
ssize_t res = 0;
while(1)
{
bzero(buf,sizeof(buf));
//接收
res = recv(newfd, buf, sizeof(buf), 0);
if(res < 0)
{
ERR_MSG("recv");
return ;
}
else if(0 == res)
{
//网络字节序的IP-->点分十进制 网络字节序的port--->本机字节序
printf("[%s : %d]pid = %d newfd = %d is off-line\n",\
inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), getpid(), newfd);
break;
}
// printf("[%s : %d] : %s\n",inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);
switch(buf[1])
{
case 1:
listdir(newfd,cin);
break;
case 2:
download(newfd, cin, buf+2);
break;
case 3:
upload(newfd, cin, buf+2,res);
break;
default:
printf("未知命令\n");break;
}
}
}
//下载函数
int download(int newfd, struct sockaddr_in cin, char* filename)
{
//准备传输数据
char buf[N];
ssize_t res = 0;
int fd = open(filename, O_RDONLY);
if(fd < 0)
{
bzero(buf,sizeof(buf));
res = sprintf(buf, "%c%c%s%c", 0, 5,"no such file", 0);
if(send(newfd, buf, res, 0) < 0)
{
ERR_MSG("send");
return -1;
}
ERR_MSG("open");
return -1;
}
while(1)
{
//填充下载数据
bzero(buf,sizeof(buf));
res = read(fd, buf, N);
if(res < 0)
{
ERR_MSG("read");
return -1;
}
else if(0 == res)
{
//传输数据个数刚好为N的整数倍时,再追加发送一个字节来表示已经发送完了
if(send(newfd, buf, 1, 0) < 0)
{
ERR_MSG("send");
return -1;
}
printf("发送完毕\n");
return 0;
}
else if(res < N)
{
if(send(newfd, buf, res, 0) < 0)
{
ERR_MSG("send");
return -1;
}
printf("发送完毕\n");
return 0;
}
if(send(newfd, buf, res, 0) < 0)
{
ERR_MSG("send");
return -1;
}
}
}
//上传函数
int upload(int newfd, struct sockaddr_in cin, char* filename, int size)
{
//准备接收数据
char buf[N];
ssize_t res = 0;
int fd = -1;
int flag = 0;
//处理粘包现象
int len = strlen(filename);
char* ptr = filename +len+1;
if('\0' != *ptr)
{
fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0664);
if(fd < 0)
{
ERR_MSG("open");
return -1;
}
flag = 1;
res = write(fd, ptr, size-len-3);
if(res < 0)
{
ERR_MSG("write");
return -1;
}
}
while(1)
{
bzero(buf,sizeof(buf));
res = recv(newfd, buf, sizeof(buf), 0);
if(res < 0)
{
ERR_MSG("recv");
return -1;
}
else if(0 == res)
{
printf("[%s : %d] newfd = %d is off-line\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);
break;
}
//新建文件
if(0 == flag)
{
fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0664);
if(fd < 0)
{
ERR_MSG("open");
return -1;
}
flag =1;
}
res = write(fd, buf, res);
if(res < 0)
{
ERR_MSG("write");
return -1;
}
else if(res < N)
{
printf("上传完毕\n");
break;
}
}
return 0;
}
FTP客户端
头文件
#ifndef __FUNCTION_H__
#define __FUNCTION_H__
#include <stdio.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
//宏定义IP和端口号
#define IP "192.168.8.120"
#define PORT 5555
#define N 512
//宏函数包装一个带行号的错误提示
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__", __LINE__);\
perror(msg);\
}while(0)
int down(int sfd, struct sockaddr_in sin);
int up(int sfd, struct sockaddr_in sin);
int list(int sfd, struct sockaddr_in sin);
#endif
主函数
#include "./function.h"
int main(int argc, const char *argv[])
{
if(argc < 2)
{
//通过主函数传参来连接服务器
printf("请输入ip\n");
return -1;
}
//获取套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
//填充地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(argv[1]);
//连接客户端
if(connect(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("connect");
return -1;
}
char c = 0;
while(1)
{
system("clear");
//功能菜单
printf("********1.目录********\n");
printf("********2.下载********\n");
printf("********3.上传********\n");
printf("********4.退出********\n");
printf("请选择功能>>>");
c = getchar();
while(getchar() != 10);
switch(c)
{
case '1':
list(sfd,sin);
break;
case '2':
down(sfd,sin);
break;
case '3':
up(sfd,sin);
break;
case '4':
goto END;
break;
default:
printf("输入有误\n");
}
printf("请输入任意字符清屏>>>");
while(getchar() != 10);
}
END:
close(sfd);
return 0;
}
功能函数
#include "function.h"
//查看目录
int list(int sfd, struct sockaddr_in sin)
{
//填充目录请求数据报
char buf[256];
ssize_t res = 0;
int size = sprintf(buf, "%c%c", 0, 1);
res = send(sfd, buf, size, 0);
if(res < 0)
{
ERR_MSG("send");
return -1;
}
while(1)
{
res = recv(sfd, buf, sizeof(buf), 0);
if(res < 0)
{
ERR_MSG("recv");
return -1;
}
else if(0 == res)
{
printf("server is off-line\n");
exit(0);
}
if(0 == buf[0])
{
printf("目录传输完毕\n");
break;
}
printf("\t%s\n",buf);
}
return 0;
}
//上传文件
int up(int sfd, struct sockaddr_in sin)
{
//填充上传请求数据报
char buf[N];
printf("请输入要上传的文件名>>>");
char filename[20] = "";
scanf("%s",filename);
while(getchar() !=10);
int size = sprintf(buf, "%c%c%s%c", 0, 3, filename, 0);
//0,2,以字符形式写到缓冲区buf中可以保持值不变,并且是以网络字节序
//若以数字写入会被变成对应的字符数字
//发送上传请求
if(send(sfd, buf, size, 0) < 0)
{
ERR_MSG("send");
return -1;
}
printf("send success\n");
ssize_t res = 0;
int count = 0;
//打开文件
int fd = open(filename, O_RDONLY);
if(fd < 0)
{
ERR_MSG("open");
return -1;
}
while(1)
{
//填充发送数据报
bzero(buf,sizeof(buf));
res = read(fd, buf, N);
if(res < 0)
{
ERR_MSG("read");
return -1;
}
else if(res == 0)
{
printf("上传完毕,共上传%dbyte\n",count);
break;
}
if(send(sfd, buf, res, 0) < 0)
{
ERR_MSG("sendto");
return -1;
}
//计算上传文件大小
count+=res;
}
//关闭文件描述符
close(fd);
return 0;
}
//下载文件
int down(int sfd, struct sockaddr_in sin)
{
printf("请输入要下载的文件名>>>");
char filename[20] = "";
scanf("%s",filename);
while(getchar() != 10);
//填充下载请求数据报
char buf[N];
unsigned short* p1 = (unsigned short*)buf;
*p1 = htons(2);
char* p2 = buf+2;
strcat(p2, filename);
//计算传输文件的大小
int size = 2 + strlen(p2)+1 ;
//发送请求
if(send(sfd, buf, size, 0) < 0)
{
ERR_MSG("send");
return -1;
}
printf("send success\n");
//准备下载
int fd =-1,flag = 0;
ssize_t res = 0;
while(1)
{
//接收数据
bzero(buf,sizeof(buf));
res = recv(sfd, buf,sizeof(buf), 0);
if(res < 0)
{
ERR_MSG("recv");
return -1;
}
else if(0 == res)
{
printf("server is off-line\n");
exit(0);
}
//判断数据头,本机为小端存储,所以要把网络字节序的数据头转换一下
unsigned short no = ntohs(*(unsigned short*)buf);
//5为错误信息
if(5 == no)
{
printf("errno : %s\n",buf+2);
break;
}
if(0 == flag)
{
fd = open(filename,O_RDWR | O_CREAT | O_TRUNC, 0664);
if(fd < 0)
{
ERR_MSG("open");
return -1;
}
flag = 1;
}
//接收追加字节,使函数退出
if(buf[0] == '\0')
{
printf("下载完毕\n");
break;
}
//写入文件
if(write(fd, buf, res) < 0)
{
ERR_MSG("write");
return -1;
}
//接收数据字节数小于N说明下载完毕
if(res < N)
{
printf("下载完毕\n");
break;
}
}
//关闭文件描述符
close(fd);
}
测试结果
查看目录
下载文件
上传文件
退出