📄 3-3.txt
字号:
int
server_mode (const char *pidfile, struct sockaddr_in *phis_addr)
{
int ctl_sock, fd;
struct servent *sv;
int port;
static struct sockaddr_in server_addr; /* Our address. */
/*声明了该函数用到的一些局部变量,包括用于ftp控制链接的插口ctl_sock、使用accept函
数返回的socket链接的文件描述符、服务器环境sv、端口地址prot和服务器地址server_addr
等。*/
/* Become a daemon. */
#ifdef HAVE_DAEMON
if (daemon(1,1) < 0) /*调用daemon系统调用,将当前进程设置为精灵进程,因为作为一个服
务器程序,ftpd服务进程不需要控制终端。这也是绝大多数网络服务器
采取的方式。*/
#endif
{
syslog (LOG_ERR, "failed to become a daemon"); /*如果设置精灵程序不成功,输出错误
记录到系统日志*/
return -1;
}
(void) signal (SIGCHLD, reapchild); /*安装SIGCHLD信号的处理程序,以便在其子进程(具
体处理每一个客户链接的进程)结束之后,收集子进程
的退出信息。关于信号的操作,将在以后详细介绍。*/
/* Get port for ftp/tcp. */
sv = getservbyname ("ftp", "tcp"); /*为了能在正确的端口上建立ftp服务,使用getservbyname
系统调用,试图取得ftp/tcp服务的端口地址,并将结构保
存到sv结构中,*/
port = (sv == NULL) ? DEFPORT : sv->s_port; /*检查sv的值,如果sv为空,说明getservbyname
没有成功,则使用缺省的端口(ftp服务的控
制链接缺省端口号为21)。否则使用sv->sport
作为端口地址。*/
/* Open socket, bind and start listen. */
ctl_sock = socket (AF_INET, SOCK_STREAM, 0); /*调用socket系统调用打开了网络插口,并
将新创建的插口号返回给ctl_sock。Socket
调用的第一个参数AF_INET,代表使用
IPV4协议,第二个参数SOCK_STREAM,
代表使用串行,基于流的、可靠的有连接协
议,该协议支持带外数据传输机制。*/
if (ctl_sock < 0)
{
syslog (LOG_ERR, "control socket: %m");
return -1;
} /*检查socket调用是否成功执行,如果不成功,则输出错误记录到系统日志*/
/* Enable local address reuse. */
{
int on = 1;
if (setsockopt (ctl_sock, SOL_SOCKET, SO_REUSEADDR,
(char )&on, sizeof(on)) < 0) /* 调用setsockopt系统调用,设置本地地址可以重用*/,
syslog (LOG_ERR, "control setsockopt: %m"); /*如果出错,输出错误信息*/
}
/*下面设置服务器地址*/
memset (&server_addr, 0, sizeof server_addr); /*清零该数据结构*/
server_addr.sin_family = AF_INET; /*设置使用的协议*/
server_addr.sin_port = htons (port); /*设置使用的端口*/
if (bind (ctl_sock, (struct sockaddr )&server_addr, sizeof server_addr))/* 将前面创建的插口绑
定到服务器地址*/
{
syslog (LOG_ERR, "control bind: %m");
return -1; /*如果绑定失败,输出错误信息,返回*/
}
if (listen (ctl_sock, 32) < 0) /*开始侦听(帧听,请确认这两个哪个是正确的)客户端的链接请
求,参数32,指定了同时最多能接受的链接数*/
{
syslog (LOG_ERR, "control listen: %m");
return -1; /*如果出错,记录错位信息,返回。*/
}
/*下面,将当前进程的pid保存到pidfle文件中,并修改该文件的执行权限。*/
/* Stash pid in pidfile. */
{
FILE pid_fp = fopen (pidfile, "w"); /*以可写方式打开pidfle文件*/
if (pid_fp == NULL)
syslog (LOG_ERR, "can't open %s: %m", PATH_FTPDPID); /*如果打开不成功,记录错误
信息。*/
else
{
fprintf (pid_fp, "%d\n", getpid()); /*使用fprintf函数,将使用getpid()系统调用取得的进程ID
写入pidfile文件*/
fchmod (fileno(pid_fp), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); /*62行调用fchmod系统调用
设置pidfle文件的权限为,
拥有者可读写,组用户和其
他用户都只能读*/
fclose (pid_fp); /*关闭该文件*/
}
}
/*下面是一个无限循环,在循环体中,一旦侦听到有客户端链接请求后,调用fork或是vfork
创建一个新的子进程处理该客户请求,而父进程则继续侦听新的客户端链接请求。*/
/* Loop forever accepting connection requests and forking off
children to handle them. */
while (1)
{
int addrlen = sizeof (*phis_addr);
fd = accept (ctl_sock, (struct sockaddr )phis_addr, &addrlen); /*在socket上侦听客户端链接
情况。该函数是阻塞型函数,
知道有新的链接请求到了,
该函数调用才返回。下面就
是创建子进程处理该请求。
因为uCLinux中,使用vfork
替代fork函数。*/
#ifdef HAVE_WORKING_FORK
if (fork () == 0) /* child */
#else
if (vfork () == 0) /* child */
#endif /*根据宏HAVE_WORKING_FORK判断使用fork还是vfork来创建子进程*/
{ /*下面是子进程的执行代码*/
(void) dup2 (fd, 0); /*把接受到的客户连接插口文件描述符,复制到子进程的标准输入上*/
(void) dup2 (fd, 1); /*把接受到的客户连接插口文件描述符,复制到子进程的标准输出上*/
close (ctl_sock); /*关闭了控制链接的插口的文件描述符ctl_sock。这样做既可以方便子进
程的操作,又可以防止子进程错误的操作标准输入输出,而引起其他安全
问题。*/
break;
}
close (fd); /*父进程代码,该代码关闭了刚接受到的客户连接插口文件描述符,为继续接
受新的客户链接做好了准备。*/
/*父进程代码到此为止,父进程将一直在此循环接受新的客户链接,然后创建新的子进程来处
理。因为在前面设置了SIG_CHLD信号的处理函数。因此,当父进程收到SIG_CHLD信号,
父进程将调用reapchild收集子进程的退出状态。另外,当父进程收到退出信号时,则结束整
个程序的运行。 */
}
/*从现在开始,以后的代码都在子进程空间执行*/
#ifdef WITH_WRAP
/* In the child. */
if (!check_host ((struct sockaddr *)phis_addr))
return -1;
#endif /*对客户端地址进行检查*/
return fd; /*关闭fd文件描述符,因为前面已经复制了该描述符*/
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -