前面的文章介绍了如何写一个简单的时间获取客户程序,本文来实现一个一个简单的时间获取服务器程序,来实现一同工作
本文地址:http://wuyudong.com/2016/08/09/2475.html,转载请注明源地址。
代码如下:
#include "unp.h" #include <time.h> int main(int argc, char **argv) { int listenfd, connfd; struct sockaddr_in servaddr; char buff[MAXLINE]; time_t ticks; listenfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(13); /* daytime server */ Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); Listen(listenfd, LISTENQ); for ( ; ; ) { connfd = Accept(listenfd, (SA *) NULL, NULL); ticks = time(NULL); snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); Write(connfd, buff, strlen(buff)); Close(connfd); } }
代码分析
TCP套接字的创建与客户程序相同。把服务器的众所周知端口捆绑到套接字。通过填写一个网际套接字地址结构并调用bind函数,服务器的众所周知端口(对于时间获取服务是13)被捆绑到所创建的套接字。我们指定IP地址为INADDR_ANY,这样要是服务器主机有多个网络接口,服务器进程就可以在任意网络接口上接受客户连接。
调用listen函数把该套接字转换成一个监听套接字,这样来自客户的外来连接就可在该套接字上由内核接受。socket、bind和listen这3个调用步骤是任何TCP服务器准备所谓的监听描述符(listening descriptor,本例中为listenfd)的正常步骤。
常值LISTENQ在我们的unp.h头文件中定义。它指定系统内核允许在这个监听描述符上排队的最大客户连接数。
通常情况下,服务器进程在accept调用中被投入睡眠,等待某个客户连接的到达并被内核接受。TCP连接使用所谓的三路握手(three-way handshake)来建立连接。握手完毕时accept返回,其返回值是一个称为已连接描述符(connected descriptor)的新描述符(本例中为connfd)。该描述符用于与新近连接的那个客户通信。accept为每个连接到本服务器的客户返回一个新描述符。
当前时间和日期是由库函数time返回的,它实际上返回的是自Unix纪元即1970年1月1日0点0分0秒(国际标准时间)以来的秒数。下一个库函数ctime把该整数值转换成直观可读的时间格式,例如:Mon May 26 20:58:40 2003
snprintf函数在这个字符串末尾添加一个回车符和一个回行符,随后write函数把结果字符串写给客户。
如果你尚不习惯改用snprintf代替较早的sprintf函数,那么现在是学习的时候了。调用sprintf无法检查目的缓冲区是否溢出。相反,snprintf要求其第二个参数指定目的缓冲区的大小,因此可确保该缓冲区不溢出。
snprintf相对较晚才加到ANSI C标准中,在称为ISO C99的版本中引入。不过几乎所有厂商都把它作为标准C函数库的一部分提供,而且另有许多免费可得的版本可用。值得注意的是,许多网络入侵是由黑客通过发送数据,导致服务器对sprintf的调用使其缓冲区溢出而发生的。必须小心使用的函数还有gets、strcat和strcpy,通常应分别改为调用fgets、strncat和strncpy。更好的替代函数是后来才引入的strlcat和strlcpy,它们确保结果是正确终止的字符串。
Comments