linux应用开发项目

一, 项目需求

二, 源码

三, 项目总结


1. 项目需求

服务器端等待链接请求:

fengjunhui@ubuntu:~/homework/ftpserver$ ./server 192.168.1.200 9999 (指定你本机Ubuntu的ip和>5000的端口号)

server sockfd :3

注意:编译生成可执行文件之后./server 和 ./client的执行在不同的目录

客户端登录

fengjunhui@ubuntu:~/homework/ftpserver/feng$ ./client 192.168.1.200 9999 (指定你本机Ubuntu的ip和>5000的端口号)

**************************

***请输入 help 查看选项***

**************************

input your choice: >>>

input your choice: >>> help

*****************************************************

********输入/功能*************************************

********list :查看服务器所在目录的所有文件************

********get filename 下载服务器目录的文件************

********put filename: 上传文件到服务器****************

********quit :关闭客户端 *****************************

******************************************************

功能1----------------查看服务器端的文件列表信息

input your choice: >>> list

***Makefile

***server

***server.c

.......

服务器目录已经接收完毕

服务器应答

目录清单已经成功发送

功能2 --------------从服务器段下载文件到客户端

input your choice: >>> get server.c

下载完毕

ls 客户端所在目录可以看到server.c的文件

服务器提示:

文件传送完成

功能3 ------------向服务器端上传文件

input your choice: >>> put hello.c(自己定义一个文件,输出hello world就行)

上传完毕

服务器提示:

接收文件成功

client client.c client.o Makefile server server.c server.o hello.c

功能4--------------客户端退出,服务器继续等待链接

input your choice: >>> quit

服务器端打印客户端退出

2. 源码

(1)net.h文件
#ifndef _NET_H_        //防止头文件被重复包含
#define _NET_H_

#include<stdio.h>
#include<stdlib.h>
#include<strings.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<string.h>
#include<stdbool.h>
#include<signal.h>

//定义新的数据类型, 简化
typedef struct sockaddr_in Addr_in;
typedef struct sockaddr Addr;

#define ERR_HANDLER(msg) do{perror(msg);exit(EXIT_FAILURE);}while(0)
#define BACKLOG 5
#define CONFIG_FILE ".info"
#define DEBUG(msg) printf("======%s=====\n", msg);   //定义调试的宏, 很方便

//配置addr, port
struct info {
	char addr[16];
	char port[12];
};

//完成对套接字的设置, 通过标志位执行服务端和客户端不同的代码
int SockInit(char *addr, char *port, bool flags);

//该函数完成读入配置文件的内容给sockinit函数
void ConfigInit(struct info *config);

//对read和write函数封装
int Read(int fd, void *buf, size_t size);
int Write(int fd, void *buf, size_t size);

#endif
(2)file_transfer.h文件
#ifndef _NET_H_        //防止头文件被重复包含
#define _NET_H_

#include<stdio.h>
#include<stdlib.h>
#include<strings.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<string.h>
#include<stdbool.h>
#include<signal.h>

//定义新的数据类型, 简化
typedef struct sockaddr_in Addr_in;
typedef struct sockaddr Addr;

#define ERR_HANDLER(msg) do{perror(msg);exit(EXIT_FAILURE);}while(0)
#define BACKLOG 5
#define CONFIG_FILE ".info"
#define DEBUG(msg) printf("======%s=====\n", msg);   //定义调试的宏, 很方便

//配置addr, port
struct info {
	char addr[16];
	char port[12];
};

//完成对套接字的设置, 通过标志位执行服务端和客户端不同的代码
int SockInit(char *addr, char *port, bool flags);

//该函数完成读入配置文件的内容给sockinit函数
void ConfigInit(struct info *config);

//对read和write函数封装
int Read(int fd, void *buf, size_t size);
int Write(int fd, void *buf, size_t size);

#endif
linux@linux:~/homework/lv_11/file_transfer$ cat file_transfer.h
#ifndef _FILI_TRANSFER_H_
#define _FILI_TRANSFER_H_

#include "net.h"
#include<errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>

//定义枚举类型, 给消息结构体里面的type, 服务端用此判断发生的事件
enum {
	TYPE_OVER, TYPE_OK, TYPE_GET, TYPE_PUT, TYPE_LIST, TYPE_SYNC
};

//定义客户端和服务端交互的消息结构体
typedef struct file_transfer {
	uint16_t type;           //类型判断
	char file_name[BUFSIZ];     //文件名
	int file_namelen;        //文件名的长度
	int file_size;       //文件大小
	char file_boyd[BUFSIZ];	
}FTS;

//公用函数
int Send(int fd, void *buf, size_t size);
int Recv(int fd, void *buf, size_t size);


//客户端函数
int do_fileget(int sockfd, char *argv, struct file_transfer *fts);
int do_fileput(int sockfd,  char *argv, struct file_transfer *fts);
int do_filelist(int sockfd,  char *argv, struct file_transfer *fts);
int do_filesync(int sockfd,  char *argv, struct file_transfer *fts);
 
//服务端函数
int do_filegethandler(int acceptfd, struct file_transfer *fts);
int do_fileputhandler(int acceptfd, struct file_transfer *fts);
int do_filelisthandler(int acceptfd, struct file_transfer *fts);
int do_filesynchandler(int acceptfd, struct file_transfer *fts);

#endif

(3)server.c文件
#include "file_transfer.h"


int do_client(int acceptfd, struct file_transfer *fts);

int main(int argc, char *argv[]) 
{
	int sockfd, acceptfd;      //准备两个变量接收sockfd
	pid_t pid;                 //服务端并法
	struct info config;  
	struct file_transfer fts;
	Addr_in peeraddr;
	socklen_t peeraddr_size = sizeof(Addr_in);

	ConfigInit(&config);                 //初始化配置addr, port
	sockfd = SockInit(config.addr, config.port, true);      //准备套接字
	
	
	while(1) {
		//接收客户端的连接请求
		if((acceptfd = accept(sockfd, (Addr *)&peeraddr, &peeraddr_size)) < 0) {
			ERR_HANDLER("accept");	
		}
		//打印客户端的addr port
		printf("[%s %d]:connect success.\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
		
		pid = fork();          //实现并发
		if(pid < 0) {
			perror("fork");
			return -1;
		} else if(pid > 0) {
			close(acceptfd);              //父进程负责接收客户端的连接请求
		} else {
			do_client(acceptfd, &fts);	  //子进程处理客户端的需求
			close(sockfd);
		}
	}
	close(sockfd);
	return 0;
}

//服务器处理客户端的需求
int do_client(int acceptfd, struct file_transfer *fts) {

	//循环接收客户端的连接请求
	while(Recv(acceptfd, fts, sizeof(FTS)) > 0) {
		switch(fts->type) {          //以此判断客户端的需求
		case TYPE_GET:
			do_filegethandler(acceptfd, fts);
			break;
		case TYPE_PUT:
			do_fileputhandler(acceptfd, fts);
			break;
		case TYPE_LIST:
			do_filelisthandler(acceptfd, fts);
			break;
		case TYPE_SYNC:
			do_filesynchandler(acceptfd, fts);
		default:
			printf("Client data Invanid.\n");
			exit(EXIT_FAILURE);                //客户端错误导致退出
		}	
	}	

	return 0;
}
(4)client.c文件
#include "file_transfer.h"

void UserAgument(int argc, char *argv[]);  //用户输入参数的规范检查
int main(int argc, char *argv[])
{
	int sockfd;
	struct info config;     //声明配置结构体
	struct file_transfer fts;

	//check argument
	UserAgument(argc, argv);
	ConfigInit(&config);
	//准备套接字
	sockfd = SockInit(config.addr, config.port, false);    //可以执行客户和服务端对sock的需求
	switch(argv[1][1]) {         
	case 'g':
		do_fileget(sockfd, argv[2], &fts);
		break;
	case 'p':
		do_fileput(sockfd, argv[2], &fts);
		break;
	case 'l':
		do_filelist(sockfd, argv[2], &fts);
		break;
	case 's':
		do_filesync(sockfd, argv[2], &fts);
		break;
	default:
		printf("Invanid option.\n");
	}
	return 0;
}

//用户参数检查
void UserAgument(int argc, char *argv[]) {
	if(argc < 3) {
		printf("%s [-g|p|l|s][filename]\n", argv[0]);
		exit(EXIT_FAILURE);
	}

}
(5)file_transfer.c文件
#include "file_transfer.h"


/***************************************************/
/*公用函数*/
/***************************************************/
int Send(int fd, void *buf, size_t size) {
	int ret;
	do{
		ret = send(fd, buf, size, 0);
	}while(ret < 0 && errno == EINTR);
	if(ret < 0) {
		printf("send failed.\n");
		return -1;
	}

	return 0;
}

int Recv(int fd, void *buf, size_t size) {
	int ret;
	do{
		ret = recv(fd, buf, size, 0);
	}while(ret < 0 && errno == EINTR);
	if(ret < 0) {
		printf("recv failed.\n");
		return -1;
	} 

	return ret;
}



/***************************************************/
/*客户端函数*/
/***************************************************/
int do_fileget(int sockfd, char *argv, struct file_transfer *fts) {
	int fd;

	fts->type = TYPE_GET;
	strcpy(fts->file_boyd, argv);
	fts->file_namelen = strlen(argv);
	Send(sockfd, fts, sizeof(FTS));

	if((fd = open(argv, O_CREAT|O_WRONLY, 0664)) < 0) {
		perror("open failed.\n");
		return -1;
	}
	Recv(sockfd, fts, sizeof(FTS));
	write(fd, fts->file_boyd, fts->file_size);

	close(fd);
	return 0;
}

int do_fileput(int sockfd, char argv[0], struct file_transfer *fts) {

	int fd;
	struct stat st;
	fts->type = TYPE_PUT;
	strcpy(fts->file_boyd, argv);
	fts->file_namelen = strlen(argv);
	Send(sockfd, fts, sizeof(FTS));


	if((fd = open(argv, O_RDONLY)) < 0) {
		perror("open");
		return -1;
	}
	if((stat(argv, &st)) < 0) {
		perror("stat");
		return -1;
	}
	Recv(sockfd, fts, sizeof(FTS));

	if(fts->type == TYPE_OK) {
		fts->file_size = st.st_size;
		read(fd, fts->file_boyd, st.st_size);
		Send(sockfd, fts, sizeof(FTS));
	}

	close(fd);
	return 0;
}

int do_filelist(int sockfd, char *argv, struct file_transfer *fts) {
	fts->type = TYPE_LIST;
	Send(sockfd, fts, sizeof(FTS));

	while(Recv(sockfd, fts, sizeof(FTS)) > 0) {
		if(fts->type == TYPE_OVER)
			break;
		if(fts->type == TYPE_OK) {
			printf("%s\n", fts->file_boyd);
		} else {
			printf("accept failed.\n");
		}
	}
	return 0;
}

int do_filesync(int sockfd, char *argv, struct file_transfer *fts) {
	struct dirent *sd;
	DIR *dp;
	int fd;
	struct stat st;

	if((dp = opendir(".")) == NULL) {
		printf("opendir failed.\n");
		return -1;
	}
	fts->type = TYPE_SYNC;
	while((sd = readdir(dp)) != NULL) {
		strcpy(fts->file_name, sd->d_name);
		//调试
		
		if((fd = open(sd->d_name, O_RDONLY)) < 0) {
			printf("open failed.\n");
			return -1;
		}
		
		if((stat(sd->d_name, &st)) < 0) {
			printf("stat failed .\n");
			return -1;
		}
		if(sd->d_type & DT_REG && sd->d_name[0] == '.') {
			fts->file_size = st.st_size;

			do_fileput(sockfd,fts->file_name, fts);
		}
	}

	close(fd);
	
	return 0;
}



/***************************************************/
/*服务端函数*/
/***************************************************/
int do_filegethandler(int acceptfd, struct file_transfer *fts) {
	struct stat file_attr;
	int fd;		

	printf("%s\n", fts->file_boyd);
	if((stat(fts->file_boyd, &file_attr)) < 0) {
		perror("stat");
		return -1;
	}
	fts->file_size = file_attr.st_size;
	if((fd = open(fts->file_boyd, O_RDONLY)) < 0) {
		perror("open");
		return -1;
	}

	read(fd, fts->file_boyd, file_attr.st_size);
	Send(acceptfd, fts, sizeof(FTS));

	usleep(1000);
	fts->type = TYPE_OVER;
	Send(acceptfd, fts, sizeof(FTS));
	close(fd);
	return 0;
}

int do_fileputhandler(int acceptfd, struct file_transfer *fts) {
	int fd;

	printf("%s\n", fts->file_boyd);
	if((fd = open(fts->file_boyd, O_CREAT|O_WRONLY, 0664)) < 0) {
		perror("open");
		return -1;
	}

	fts->type = TYPE_OK;
	Send(acceptfd, fts, sizeof(FTS));

	Recv(acceptfd, fts, sizeof(FTS));
	write(fd, fts->file_boyd, fts->file_size);

	close(fd);
	return 0;	
}

int do_filelisthandler(int acceptfd, struct file_transfer *fts) {

	DIR *dp;
	struct dirent *sd;
	if((dp = opendir(".")) == NULL) {
		printf("opendir failed.\n");
		return -1;
	}
	
	fts->type = TYPE_OK;
	while((sd = readdir(dp)) != NULL) {	
		strcpy(fts->file_boyd, sd->d_name);
		Send(acceptfd, fts, sizeof(FTS));
	}
	fts->type = TYPE_OVER;
	Send(acceptfd, fts, sizeof(FTS));

	return 0;
}

int do_filesynchandler(int acceptfd, struct file_transfer *fts) {
	int fd;
	//debug
	printf("%s\n", fts->file_name);

	printf("%s\n", fts->file_boyd);

	if((fd = open(fts->file_name, O_CREAT|O_WRONLY), 0220) < 0) {
		printf("open failed .\n");
		return -1;
	}

	write(fd, fts->file_boyd, fts->file_size);

	close(fd);
	return 0;
}
(6)Init.c文件
#include "net.h" 

void ConfigInit(struct info *config) {
	FILE *fp;
	int len;

	if((fp = fopen(CONFIG_FILE, "r")) == NULL) {
		printf("fopen failed.\n");
		return ;
	}

	//first
	if((fgets(config->addr, 16, fp)) == NULL) {
		printf("fgets addr failed.\n");
		return ;
	}
	
	len = strlen(config->addr);
	if(config->addr[len] == '\n')
		config->addr[len] = '\0';
	//second
	if((fgets(config->port, 12, fp)) == NULL) {
		printf("fgets port failed.\n");
		return ;
	}
	
	len = strlen(config->port);
	if(config->port[len] == '\n')
		config->port[len] = '\0';
	
	return ;
}

int SockInit(char *addr, char *port, bool flags) {
	int sockfd;
	Addr_in serveraddr;

	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
		ERR_HANDLER("socket");
	bzero(&serveraddr, sizeof(Addr_in));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(atoi(port));
	if(!(inet_aton(addr, &serveraddr.sin_addr)));

	//server
	if(flags) {
		//设置套接字属性
		int attr_on = 1;
		setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &attr_on, sizeof(int ));
		if((bind(sockfd, (Addr *)&serveraddr, sizeof(Addr_in))) < 0)
			ERR_HANDLER("bind");
		if(listen(sockfd, BACKLOG) < 0)
			ERR_HANDLER("listen");
	} 
	//client
	else {
		if(connect(sockfd, (Addr *)&serveraddr, sizeof(Addr_in)) < 0)
			ERR_HANDLER("connect");
	}

	return sockfd;
}

3. 项目总结

(1)对于c语言知识的很多禁区有了全新的认识, 在项目中看似简单的内容, 往往是导致项目过程中影响开发效率的最大因素, 不可以轻视很多小知识, 这样会使你在开发过程中debug到爆炸,从而心态爆炸。

(2)编写makefil, 因为整个程序的过程中需要大量的调试, 不断的编译, 手动编译你会疯的

(3)makefile如下

CC=gcc
CFLAGS=-g -Wall
all:server client
server:server.c file_transfer.c Init.c
	gcc -g server.c file_transfer.c Init.c -o server -Wall
client:client.c file_transfer.c Init.c
	gcc -g client.c file_transfer.c Init.c -o client -Wall

clean:
	rm server client
cp:
	cp client .info /home/linux/homework/lv_11/file_transfer/test/

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@daiwei

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值