miniftp项目

1.系统框架搭建
common.h<头文件定义>
sysutil<公有工具定义>

int tcp_server(const char *host, unsigned short port);
int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds);

miniftp<检测是否root启动,主进程完成客户端的连接,以及子进程的创建,开启会话>

//检测是否root启动,主进程完成客户端的连接,
//以及子进程的创建, 开启会话
#include"miniftp.h"

int main(int argc, char *argv[])
{
	//首先判断是否是root权限启动
	if(getuid() != 0)
	{
		printf("miniftp : must be started as root.\n");
		exit(EXIT_FAILURE);
	}
	//定义会话变量并进行初始化
	session_t sess = 
		{
			-1,
			-1, -1
		};
	//定义一个监听套接字,监听服务器
	int listenfd = tcp_server(NULL, 9188);

	pid_t pid;
	int conn;
	struct sockaddr_in addrcli;
	//接收客户端的连接,每当接受到一个新的连接,就创建一个进程
	while(1)
	{
		//将一个已连接套接字返回给conn
		if((conn=accept_timeout(listenfd, &addrcli, 0)) < 0)
			ERR_EXIT("accept_timeout");

		pid = fork();
		//创建进程失败
		if(pid == -1)
			ERR_EXIT("fork");
		
		//如果创建的进程是子进程,就开始创建会话
		//ftp服务进程(子进程)
		if(pid == 0)
		{
			close(listenfd);
			sess.ctrl_fd = conn;
			begin_session(&sess);
			//开始会话,包含两个进程,nobody进程和ftp服务进程
		}
		//nobody进程(父进程)
		else
		{
			//关闭已连接套接字
			close(conn);
		}
	}
	//关闭监听套接字
	close(listenfd);
	return 0;
}

session<会话结构的定义,实现会话函数,创建子进程,区分出ftp进程和nobody进程,实现ftp和nobody之间的通讯连接,更改nobody进程getpwnam>

//会话结构的定义,实现会话函数,创建子进程,
//区分出ftp进程和nobody进程,实现ftp与nobody之间的通讯连接
//更改nobody进程getpwnam

#include"session.h"
#include"ftpproto.h"
#include"privparent.h"

//会话函数,进行通信
void begin_session(session_t *sess)
{
	int sockfds[2];
	//函数原型:int socketpair(int domin, int type, int protocol, int socket_vector[2]);
	//函数中四个参数说明:协议族、套接口的类型、使用的协议、指向存储文件描述符的指针
	//第一个参数,表示协议族,只能为AF_LOCAL或者AF_UNIX。
	//第二个参数,表示套接口的类型,可以是SOCK_STREAM(管道流)或者SOCK_DGRAM,这里的管道是双向的
	//即每一端都可以读写。
	//第三个参数表示协议,只能为0.
	//第四个参数是接受两个套接口的整数数组,建立的两个套接字描述符会放在sockfds[0],sockfds[1]中。
	//函数返回值:如果成功返回0,失败返回-1,并用errno来表示特定的错误号。
	//功能:实现在同一个文件描述符中进行读写(全双工通信)
	socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds);

	pid_t pid;//定义一个进程ID变量
	//创建进程,1则为父进程,0为子进程,失败则返回-1。
	pid = fork();
	if(pid == -1)
		ERR_EXIT("fork");
	if(pid == 0)
	{
		//ftp 服务进程(子进程)
		close(sockfds[0]);
		handle_child(sess);
	}
	else
	{
		//nobody 进程(父进程)
		close(sockfds[1]);
		handle_parent(sess);
	}
}

sysutil

#include"sysutil.h"


/**
 * tcp_server - 启动tcp服务器
 * @host: 服务器IP地址或者服务器主机名
 * @port: 服务器端口
 * 成功返回监听套接字
 */
int tcp_server(const char *host, unsigned short port)
{
	int listenfd; //监听套接字
	//(PF_INET网际协议, SOCK_STREAM(套接字类型-流式套接字), 
	//0(自动选择TCP协议));
	if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
		ERR_EXIT("tcp_server");

	struct sockaddr_in servaddr;//地址套接字变量
	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;//地址家族-AF_INET
	if (host != NULL) 
	{
		//inet_aton 将点分十进制的IP地址转换成一个网络字节序的32位的整数,
		//将host的转换IP地址sinaddr放到servaddr.sin_addr里,若返回值等于零
		//则返回的是无效的IP地址,说明host很可能是服务器的主机名,则我们
		//可以根据主机名称得到IP地址
		if (inet_aton(host, &servaddr.sin_addr) == 0) 
		{
			struct hostent *hp;
			//通过主机名获得所有的IP地址
			hp = gethostbyname(host);
			if (hp == NULL)
				ERR_EXIT("gethostbyname");
			//强转类型再取地址放到servaddr.sin_addr中去,这样就得到了IP地址。
			servaddr.sin_addr = *(struct in_addr*)hp->h_addr;
		}
	}
	//如果host是空指针,则我们认为它绑定的是本地所有的IP地址。
	//htonl() 函数原型为 unit32_t htonl(unit32_t hostlong);
	//函数名中h-host 主机地址,to-to转换,n-net网络, l-unsigned long无符号的长整型;
	//函数的返回值是一个32位的网络字节顺序;
	//函数的作用是将一个32位数从主机字节顺序转换成网络字节序,简单来说,就是把一个
	//32位数高低位互换。
	else
		servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//填充IP地址
	
	//htonl() 函数原型为 unit16_t htons(unit16_t hostlong);
	//函数名中h-host 主机地址,to-to转换,n-net网络, s-signed long无符号的短整型;
	//函数的返回值是一个16位的网络字节顺序;
	//函数的作用是将一个16位数从主机字节顺序转换成网络字节序,简单来说,就是把一个
	//16位数高低位互换。
	servaddr.sin_port = htons(port);//填充端口号

	int on = 1;//设置地址重复利用
	if ((setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on))) < 0)
		ERR_EXIT("setsockopt");
	//绑定
	if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
		ERR_EXIT("bind");
	//监听
	if (listen(listenfd, SOMAXCONN) < 0)
		ERR_EXIT("listen");

	return listenfd;
}

/**
 * accept_timeout - 带超时的accept
 * @fd: 套接字
 * @addr: 输出参数,返回对方地址
 * @wait_seconds: 等待超时秒数,如果为0表示正常模式
 * 成功(未超时)返回已连接套接字,超时返回-1并且errno = ETIMEDOUT
 */
int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
	int ret;
	socklen_t addrlen = sizeof(struct sockaddr_in);

	if (wait_seconds > 0) 
	{
		fd_set accept_fdset;
		struct timeval timeout;
		FD_ZERO(&accept_fdset);
		FD_SET(fd, &accept_fdset);
		timeout.tv_sec = wait_seconds;
		timeout.tv_usec = 0;
		do {
			ret = select(fd + 1, &accept_fdset, NULL, NULL, &timeout);
		} while (ret < 0 && errno == EINTR);
        if (ret == -1) {
			return -1;
        }
		else if (ret == 0) {
			errno = ETIMEDOUT;
			return -1;
		}
	}

	if (addr != NULL)
		ret = accept(fd, (struct sockaddr*)addr, &addrlen);
	else
		ret = accept(fd, NULL, NULL);

	return ret;
}

ftpproto<ftp进程>
priparent<nobody进程>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值