Linux网络编程学习笔记_1_一个简略的时间获取服务器/客户端程序及解析

Linux网络编程学习笔记_1_一个简单的时间获取服务器/客户端程序及解析

    终于自己也可以写出一个服务器/客户端程序,虽然很简单,虽然只是Richard Stevens大神代码的简单复制,但是对于我来说确实一件开天劈地的大事,因为这是第一个,他让我意识到自己原来也可以写网络程序!

    好了,废话不多说,代码如下:

1、服务器端:

#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;

    /*
    创建一个网际(AF_INET)字节流(SOCK_STREAM)套接字,
    并以listenfd返回其描述符。
    */
    listenfd = Socket(AF_INET,SOCK_STREAM,0);

    /*
    通过填写一个网际套接字地址结构并调用bind函数,将服务器的一个端口
    绑定到所创建的套接字上。
    */
    bzero(&servaddr,sizeof(servaddr));//bzero将整个结构体清零
    servaddr.sin_family = AF_INET;//将地址族设置为AF_INET
    /*
    INADDR_ANY:指定IP地址为INADDR_ANY,这样要是服务器主机
    有多个网络接口,服务器进程就可一在任意网路接口上接受连接。
    */
    servaddr.sin_addr.s_addr =htonl(INADDR_ANY);
	/*
	将服务器开放的端口设定为9999,原文中设定的是13,
	如果按照原文的示例,则该程序运行时需要获取root权限,才能申请使用13号端口
	但是在将服务器开放端口设为9999之后,需要在客户端程序的访问端口处,
	改为9999,不然会出现访问受限的情况!
	网际套接字地址结构中IP地址和端口号这两个成员必须使用特定格式,
	为此我们使用htons("主机到网络短整数")去转换二进制端口号。
	*/
    servaddr.sin_port = htons(9999);

    Bind(listenfd,(SA *) &servaddr,sizeof(servaddr));

    /*
    调用listen函数将该套接字转换成一个监听套接字,
    这样来自客户的外来连接就可以在该套接字上有内核接受
    */
    //LISTENQ指定系统内核允许在这个监听描述符上排队的最大客户连接数
    Listen(listenfd,LISTENQ);

	/*
	接受客户连接,发送应答
	*/
    for(;;)
    {
    	/*
    	TCP的三路握手,进行连接。
    	完成TCP连接之后,accept函数返回一个“已连接描述符”
		该描述符用于与新近连接的那个客户端进行通信。
		accept为每个连接到本服务器的客户返回一个新的描述符。
    	*/
        connfd = Accept(listenfd,(struct sockaddr *)NULL,NULL);

		//获取当前系统时间
        ticks = time(NULL);
        //ctime将整数值转换成直观可读的时间格式
        snprintf(buff,sizeof(buff),"%.24s\r\n",ctime(&ticks));
        //write函数将把结果字符串写给客户
        Write(connfd,buff,strlen(buff));

		//终止连接,TCP的四次挥手
        Close(connfd);
    }

    exit(0);
}

2、客户端

#include "unp.h"

int main(int argc,char **argv)
{
	//如果程序参数格式不正确
	if (argc != 2)
	{
		err_quit("usage: a.out <IPaddress>");
	}

	//创建一个TCP套接字,返回sockfd作为套接字描述符
	int sockfd;
	if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
	{
		err_sys("socket error");
	}

	/*
	指定服务器的IP地址与端口
	*/
	struct sockaddr_in servaddr;
	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(9999);//解释见服务器程序
	//inet_pton("呈现形式到数值"),把ASCII命令行参数转换为合适的格式
	//argv[1]代表服务器IP地址
	if (inet_pton(AF_INET,argv[1],&servaddr.sin_addr) < 0)
	{
		err_quit("inet_pton err for %s",argv[1]);
	}

	/*
	建立与服务器的连接
	connect函数应用于一个TCP套接字时,将与由他的第二个参数只想的套接字地址结构
	指定的服务器建立一个TCP连接。
	在unp.h头文件中,将SA定义为struct sockaddr。
	*/
	if (connect(sockfd,(SA *)&servaddr,sizeof(servaddr)) < 0)
	{
		err_sys("connect error");
	}

	/*
	读入并输出服务器的应答:
	**使用TCP时必须小心,因为TCP是一个没有记录边界的字节流协议
	*/
	int n;
	char recvline[MAXLINE + 1];
	//如果数据量很大,我们不能保证一次read就可一返回服务器的所有应答。
	//当read返回0(对端关闭连接)或者负值(发生错误)时,终止循环。
	while ((n = read(sockfd,recvline,MAXLINE)) > 0)
	{
		recvline[n] = 0;
		if (fputs(recvline,stdout) == EOF)
		{
			err_sys("fputs error");
		}
	}
	if (n < 0)
	{
		err_sys("read error");
	}

	/*
	exit终止程序运行,UNIX在一个进程终止时总是关闭该进程所有打开的
	描述符,我们的TCP套接字就此关闭!
	*/
	exit(0);
}

3、运行效果

Linux网络编程学习笔记_1_一个简略的时间获取服务器/客户端程序及解析