ser.c(服务器):
#include "./fun.h"
int main(int argc,const char * argv[])
{
//1.判断入参
if(argc!=3){
fprintf(stderr,"入参为空,请检查\n");
return -1;
}
//端口号转整型
int port=atoi(argv[2]);
//变量声明
struct sockaddr_in sin;
int sinLen=sizeof(sin);
struct sockaddr_in cin;
int cinLen=sizeof(cin);
Info_t info;
int newfd;
pthread_t tid;
//2.创建套接字
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd==-1){
PRINT_ERR("socket error");
}
//3.快速重用端口号
int optval=1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval))==-1){
PRINT_ERR("setsockopt error");
}
//3.填充服务器结构体信息
sin.sin_family=AF_INET;
sin.sin_addr.s_addr=inet_addr(argv[1]);
sin.sin_port=htons(port);
//4.绑定
if(bind(sfd,(struct sockaddr *)&sin,sinLen)==-1){
PRINT_ERR("bind error");
}
//5.监听
if(listen(sfd,10)==-1){
PRINT_ERR("listen error");
}
//6.多线程并发
while(1){
newfd=accept(sfd,(struct sockaddr *)&cin,&sinLen);
if(newfd==-1){
PRINT_ERR("accept error");
}
info.newfd=newfd;
//创建线程,将信息结构体传入线程
if(pthread_create(&tid,NULL,callBack,&info)==-1){
PRINT_ERR("pthread_create error");
}
}
//7.关闭套接字
close(sfd);
return 0;
}
cli.c(客户端):
#include "./fun.h"
int main(int argc, const char *argv[])
{
// 1.判断入参
if (argc != 3) {
fprintf(stderr, "入参为空,请检查\n");
return -1;
}
//变量声明
struct sockaddr_in sin;
int sinLen = sizeof(sin);
buf_t buf;
char path[128] = "";
// 2.端口号转整型
int port = atoi(argv[2]);
// 3.创建套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd == -1) {
PRINT_ERR("socket error");
}
// 4.填充结构体信息
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(argv[1]);
sin.sin_port = htons(port);
// 5.连接服务器
if (connect(sfd, (struct sockaddr *)&sin, sinLen) == -1) {
PRINT_ERR("connect error");
}
while (1) {
// 6.打印选项菜单
puts("*****************************");
puts("*********1.查看文件***********");
puts("*********2.下载文件***********");
puts("*********3.上传文件***********");
puts("*****************************");
printf("请输入选项:>>>");
memset(&buf, 0, sizeof(buf));
memset(path, 0, sizeof(path));
scanf("%d", &buf.type);
getchar();
switch (buf.type) {
case 1:
break;
case 2:
if (getcwd(path, sizeof(path)) == NULL) {
PRINT_ERR("getcwd error");
}
strcpy(buf.data1, path);
strcat(buf.data1, "/download/");
printf("请输入要下载的文件名:>>>");
if (fgets(buf.data2, sizeof(buf.data2), stdin) == NULL) {
PRINT_ERR("fgets error");
}
buf.data2[strlen(buf.data2) - 1] = '\0';
strcat(buf.data1, buf.data2);
break;
case 3:
if (getcwd(path, sizeof(path)) == NULL) {
PRINT_ERR("getcwd error");
}
strcpy(buf.data1, path);
strcat(buf.data1, "/download/");
printf("请输入要上传的文件名:>>>");
if (fgets(buf.data2, sizeof(buf.data2), stdin) == NULL) {
PRINT_ERR("fgets error");
}
buf.data2[strlen(buf.data2) - 1] = '\0';
strcat(buf.data1, buf.data2);
break;
default:
printf("error");
}
// 7.发送消息
if (send(sfd, &buf, sizeof(buf), 0) == -1) {
PRINT_ERR("send error");
}
// printf("1\n");
// 8.接收消息
while (1) {
memset(&buf, 0, sizeof(buf));
if (recv(sfd, &buf, sizeof(buf), 0) == -1) {
PRINT_ERR("recv error");
}
// buf.data2是quit说明服务器读取文件名已经读完,退出循环
if (strcmp(buf.data2, "quit") == 0)
break;
puts(buf.data1);
if (buf.type == 2 || buf.type == 3)
break;
}
//按任意键继续
printf("<<请按回车键继续>>");
getchar();
}
// 8.关闭套接字
close(sfd);
return 0;
}
fun.h(头文件):
#ifndef __FUN_H__
#define __FUN_H__
#include <head.h>
/********共用***********/
//结构体声明
//线程信息结构体
typedef struct{
int newfd;
}Info_t;
//消息收发结构体
typedef struct{
int type;
char data1[256];
char data2[256];
}buf_t;
/********服务器端***********/
//函数声明
void *callBack(void *arg);
//显示文件
void showFile(int newfd);
//下载文件
void downloadFile(int newfd,buf_t buf);\
//上传文件
void uploadFile(int newfd,buf_t buf);
/********客户端***********/
#endif
fun.c(功能文件):
#include "fun.h"
void *callBack(void *arg)
{
//转分离态
pthread_detach(pthread_self());
Info_t info = *(Info_t *)arg;
int newfd = info.newfd;
//变量声明
buf_t buf;
//循环收发数据
while (1) {
if (recv(newfd, &buf, sizeof(buf), 0) == -1) {
VPRINT_ERR("recv error");
}
switch (buf.type) {
case 1:
showFile(newfd);
break;
case 2:
downloadFile(newfd, buf);
break;
case 3:
uploadFile(newfd, buf);
break;
default:
printf("error");
}
}
//关闭文件描述符
close(newfd);
//退出线程
pthread_exit(0);
}
//显示文件
void showFile(int newfd)
{
//变量声明
DIR *dir = NULL;
buf_t buf;
struct dirent *dp = NULL;
//打开目录
dir = opendir("./");
if (NULL == dir) {
VPRINT_ERR("opendir error");
}
while (1) {
//循环读取目录,直到读完
dp = readdir(dir);
if (dp == NULL) {
if (errno == 0) {
strcpy(buf.data2, "quit");
if (send(newfd, &buf, sizeof(buf), 0) == -1) {
VPRINT_ERR("send error");
}
break;
} else {
VPRINT_ERR("readdir error");
}
}
memset(&buf, 0, sizeof(buf));
buf.type = 1;
strcpy(buf.data1, dp->d_name);
//循环发送
if (send(newfd, &buf, sizeof(buf), 0) == -1) {
VPRINT_ERR("send error");
}
}
//关闭目录,dir指向NULL
closedir(dir);
dir = NULL;
}
//下载文件
void downloadFile(int newfd, buf_t buf)
{
//变量声明
char server_path[256] = "./";
char client_path[256] = "";
char str[128] = "";
int ret;
strcpy(client_path, buf.data1);
strcat(server_path, buf.data2);
// 1.打开2个文件
int fdr = open(server_path, O_RDONLY);
int fdw = open(client_path, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (fdr == -1 || fdw == -1) {
VPRINT_ERR("open error");
}
while (1) {
// 2.循环读取
ret = read(fdr, str, sizeof(str));
if (ret == -1) {
VPRINT_ERR("read error");
} else if (ret == 0) {
break;
}
// 3.循环写入
write(fdw, str, ret);
}
// 4.发送消息
memset(&buf, 0, sizeof(buf));
buf.type = 2;
strcpy(buf.data1, "下载成功");
if (send(newfd, &buf, sizeof(buf), 0) == -1) {
VPRINT_ERR("send error");
}
// 5.关闭2个文件
close(fdr);
close(fdw);
}
//上传文件
void uploadFile(int newfd, buf_t buf)
{
//变量声明
char server_path[256] = "./";
char client_path[256] = "";
char str[128] = "";
int ret;
strcpy(client_path, buf.data1);
strcat(server_path, buf.data2);
// 1.打开2个文件
int fdr = open(client_path, O_RDONLY);
int fdw = open(server_path, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (fdr == -1 || fdw == -1) {
VPRINT_ERR("open error");
}
while (1) {
// 2.循环读取
ret = read(fdr, str, sizeof(str));
if (ret == -1) {
VPRINT_ERR("read error");
} else if (ret == 0) {
break;
}
// 3.循环写入
write(fdw, str, ret);
}
// 4.发送消息
memset(&buf, 0, sizeof(buf));
buf.type = 3;
strcpy(buf.data1, "上传成功");
if (send(newfd, &buf, sizeof(buf), 0) == -1) {
VPRINT_ERR("send error");
}
// 5.关闭2个文件
close(fdr);
close(fdw);
}
工程管理文件(makefile):
-include config.mk
#版本1
# a.out:$(NAME).o $(FUN).o
# $(CC) $^ -o $@
# %.o:%.c
# $(CC) -c $^ -o $@
# .PHONY:clean
# clean:
# rm -rf $(NAME).o $(FUN).o a.out
#版本2(生成动态库版本)
# a.out:$(NAME).o lib$(FUN).so
# $(CC) $< -L./lib -l$(FUN) -o $@
# lib$(FUN).so:$(FUN).o
# $(CC) -shared $^ -o $@
# mv $@ ./lib
# $(NAME).o:$(NAME).c
# $(CC) -c $^ -o $@
# $(FUN).o:$(FUN).c
# $(CC) -Wall -fPIC -c $^ -o $@
# .PHONY:clean
# clean:
# rm -rf *.o lib/*.so a.out
#版本3(生成动态库版本+链接线程库版)
# a.out:$(NAME).o lib$(FUN).so
# $(CC) $< -L./lib -l$(FUN) -o $@ -lpthread
# lib$(FUN).so:$(FUN).o
# $(CC) -shared $^ -o $@
# mv $@ ./lib
# $(NAME).o:$(NAME).c
# $(CC) -c $^ -o $@
# $(FUN).o:$(FUN).c
# $(CC) -Wall -fPIC -c $^ -o $@
# .PHONY:clean
# clean:
# rm -rf *.o lib/*.so a.out
#版本4(生成动态库版本+非父子进程通信版本)
# .PHONY:all
# all:F A B
# A:$(NAMEA).o lib$(FUN).so
# $(CC) $< -L./lib -l$(FUN) -o $@
# B:$(NAMEB).o lib$(FUN).so
# $(CC) $< -L./lib -l$(FUN) -o $@
# F:$(NAMEF).o lib$(FUN).so
# $(CC) $< -L./lib -l$(FUN) -o $@
# lib$(FUN).so:$(FUN).o
# $(CC) -shared $^ -o $@
# mv $@ ./lib
# $(NAMEF).o:$(NAMEF).c
# $(CC) -c $^ -o $@
# $(NAMEA).o:$(NAMEA).c
# $(CC) -c $^ -o $@
# $(NAMEB).o:$(NAMEB).c
# $(CC) -c $^ -o $@
# $(FUN).o:$(FUN).c
# $(CC) -Wall -fPIC -c $^ -o $@
# .PHONY:clean
# clean:
# rm -rf *.o lib/*.so A B F
#版本5(生成动态库版本+非父子进程通信版本+链接线程库版本)
# .PHONY:all
# all:F A B
# A:$(NAMEA).o lib$(FUN).so
# $(CC) $< -L./lib -l$(FUN) -o $@ -lpthread
# B:$(NAMEB).o lib$(FUN).so
# $(CC) $< -L./lib -l$(FUN) -o $@ -lpthread
# F:$(NAMEF).o lib$(FUN).so
# $(CC) $< -L./lib -l$(FUN) -o $@
# lib$(FUN).so:$(FUN).o
# $(CC) -shared $^ -o $@
# mv $@ ./lib
# $(NAMEF).o:$(NAMEF).c
# $(CC) -c $^ -o $@
# $(NAMEA).o:$(NAMEA).c
# $(CC) -c $^ -o $@
# $(NAMEB).o:$(NAMEB).c
# $(CC) -c $^ -o $@
# $(FUN).o:$(FUN).c
# $(CC) -Wall -fPIC -c $^ -o $@
# .PHONY:clean
# clean:
# rm -rf *.o lib/*.so A B F
#版本6(生成动态库版本+客户端服务器通信版本)
# .PHONY:all
# all:cli ser
# ser:$(NAMESER).o lib$(FUN).so
# $(CC) $< -L./lib -l$(FUN) -o $@
# cli:$(NAMECLI).o lib$(FUN).so
# $(CC) $< -L./lib -l$(FUN) -o $@
# lib$(FUN).so:$(FUN).o
# $(CC) -shared $^ -o $@
# mv $@ ./lib
# $(NAMESER).o:$(NAMESER).c
# $(CC) -c $^ -o $@
# $(NAMECLI).o:$(NAMECLI).c
# $(CC) -c $^ -o $@
# $(FUN).o:$(FUN).c
# $(CC) -Wall -fPIC -c $^ -o $@
# .PHONY:clean
# clean:
# rm -rf *.o lib/*.so ser cli
#版本6(生成动态库版本+客户端服务器通信版本+线程库版本)
.PHONY:all
all:cli ser
ser:$(NAMESER).o lib$(FUN).so
$(CC) $< -L./lib -l$(FUN) -o $@ -lpthread
cli:$(NAMECLI).o lib$(FUN).so
$(CC) $< -L./lib -l$(FUN) -o $@ -lpthread
lib$(FUN).so:$(FUN).o
$(CC) -shared $^ -o $@
mv $@ ./lib
$(NAMESER).o:$(NAMESER).c
$(CC) -c $^ -o $@
$(NAMECLI).o:$(NAMECLI).c
$(CC) -c $^ -o $@
$(FUN).o:$(FUN).c
$(CC) -Wall -fPIC -c $^ -o $@
.PHONY:clean
clean:
rm -rf *.o lib/*.so ser cli
工程管理配置文件(config.mk):
#对应版本1,2,3的makefile
# CC=gcc
# NAME=test
# FUN=fun
#对应版本4,5的makefile
# CC=gcc
# NAMEA=Atest
# NAMEB=Btest
# NAMEF=Ftest
# FUN=fun
#对应版本6的makefile
CC=gcc
NAMESER=ser
NAMECLI=cli
FUN=fun
测试结果:
编译makefile:
打开服务器和客户端:
上传和下载文件的位置:
选择1查看文件:
选择2下载文件:
另起一个终端客户端,选择3上传文件:
关闭服务器和客户端,diff测试下载以及上传的文件是否和原来的一致: