• Linux 网络编程: daytime Service


    前言

    如果你这段时间过得很舒服,那就证明你荒废以一段时间。如果你这段时间过得很辛苦,那么恭喜,你又进步了。最近入党的事情忙得焦头烂额,博客也拖了好久没写,主要也是因为要装 xinetd 服务一直没装好,Mac 上也无法编译多个文件,于是我还特意租了一个月服务器。OK,现在来实现客户端链接主机,从主机获取时间。

    Error

    errexit.c

    /* errexit.c - errexit */
    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    /*------------------------------------------------------------------------
     * errexit - print an error message and exit
     *------------------------------------------------------------------------
     */
    int errexit(const char *format, ...)
    {
    	va_list	args;
    
    	va_start(args, format);
    	vfprintf(stderr, format, args);
    	va_end(args);
    	exit(1);
    }
    
    

    errno.h

    /* Error constants.  Linux specific version.
       Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
       This file is part of the GNU C Library.
    
       The GNU C Library is free software; you can redistribute it and/or
       modify it under the terms of the GNU Lesser General Public
       License as published by the Free Software Foundation; either
       version 2.1 of the License, or (at your option) any later version.
    
       The GNU C Library is distributed in the hope that it will be useful,
       but WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       Lesser General Public License for more details.
    
       You should have received a copy of the GNU Lesser General Public
       License along with the GNU C Library; if not, write to the Free
       Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
       02111-1307 USA.  */
    
    #ifdef _ERRNO_H
    
    # undef EDOM
    # undef EILSEQ
    # undef ERANGE
    # include <linux/errno.h>
    
    /* Linux has no ENOTSUP error code.  */
    # define ENOTSUP EOPNOTSUPP
    
    /* Linux also had no ECANCELED error code.  Since it is not used here
       we define it to an invalid value.  */
    # ifndef ECANCELED
    #  define ECANCELED	125
    # endif
    
    # ifndef __ASSEMBLER__
    /* Function to get address of global `errno' variable.  */
    extern int *__errno_location (void) __THROW __attribute__ ((__const__));
    
    #  if !defined _LIBC || defined _LIBC_REENTRANT
    /* When using threads, errno is a per-thread value.  */
    #   define errno (*__errno_location ())
    #  endif
    # endif /* !__ASSEMBLER__ */
    #endif /* _ERRNO_H */
    
    #if !defined _ERRNO_H && defined __need_Emath
    /* This is ugly but the kernel header is not clean enough.  We must
       define only the values EDOM, EILSEQ and ERANGE in case __need_Emath is
       defined.  */
    # define EDOM	33	/* Math argument out of domain of function.  */
    # define EILSEQ	84	/* Illegal byte sequence.  */
    # define ERANGE	34	/* Math result not representable.  */
    #endif /* !_ERRNO_H && __need_Emath */
    
    

    Server

    passivesock.c

    /* passivesock.c - passivesock */
    
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <stdlib.h>
    #include <string.h>
    #include <netdb.h>
    #include <errno.h>
    extern int	errno;
    
    int	errexit(const char *format, ...);
    
    unsigned short	portbase = 0;	/* port base, for non-root servers	*/
    
    /*------------------------------------------------------------------------
     * passivesock - allocate & bind a server socket using TCP or UDP
     *------------------------------------------------------------------------
     */
    int passivesock(const char *service, const char *transport, int qlen)
    /*
     * Arguments:
     *      service   - service associated with the desired port
     *      transport - transport protocol to use ("tcp" or "udp")
     *      qlen      - maximum server request queue length
     */
    {
    	struct servent	*pse;	/* pointer to service information entry	*/
    	struct protoent *ppe;	/* pointer to protocol information entry*/
    	struct sockaddr_in sin;	/* an Internet endpoint address		*/
    	int	s, type;	/* socket descriptor and socket type	*/
    
    	memset(&sin, 0, sizeof(sin));
    	sin.sin_family = AF_INET;
    	sin.sin_addr.s_addr = INADDR_ANY;
    
        /* Map service name to port number */
    	if ( pse = getservbyname(service, transport) )
    		sin.sin_port = htons(ntohs((unsigned short)pse->s_port)
    			+ portbase);
    	else if ((sin.sin_port=htons((unsigned short)atoi(service))) == 0)
    		errexit("can't get "%s" service entry
    ", service);
    
        /* Map protocol name to protocol number */
    	if ( (ppe = getprotobyname(transport)) == 0)
    		errexit("can't get "%s" protocol entry
    ", transport);
    
        /* Use protocol to choose a socket type */
    	if (strcmp(transport, "udp") == 0)
    		type = SOCK_DGRAM;
    	else
    		type = SOCK_STREAM;
    
        /* Allocate a socket */
    	s = socket(PF_INET, type, ppe->p_proto);
    	if (s < 0)
    		errexit("can't create socket: %s
    ", strerror(errno));
    
        /* Bind the socket */
    	if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
    		errexit("can't bind to %s port: %s
    ", service,
    			strerror(errno));
    	if (type == SOCK_STREAM && listen(s, qlen) < 0)
    		errexit("can't listen on %s port: %s
    ", service,
    			strerror(errno));
    	return s;
    }
    
    

    passiveTCP.c

    /* passiveTCP.c - passiveTCP */
    
    int	passivesock(const char *service, const char *transport,
    		int qlen);
    
    /*------------------------------------------------------------------------
     * passiveTCP - create a passive socket for use in a TCP server
     *------------------------------------------------------------------------
     */
    int passiveTCP(const char *service, int qlen)
    /*
     * Arguments:
     *      service - service associated with the desired port
     *      qlen    - maximum server request queue length
     */
    {
    	return passivesock(service, "tcp", qlen);
    }
    
    

    TCPdaytimed.c

    /* TCPdaytimed.c - main */
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
    extern int	errno;
    int		errexit(const char *format, ...);
    void		TCPdaytimed(int fd);
    int		passiveTCP(const char *service, int qlen);
    
    #define QLEN	32
    
    /*------------------------------------------------------------------------
     * main - Iterative TCP server for DAYTIME service
     *------------------------------------------------------------------------
     */
    int main(int argc, char *argv[])
    {
    	struct	sockaddr_in fsin;	/* the from address of a client	*/
    	char	*service = "daytime";	/* service name or port number	*/
    	int	msock, ssock;		/* master & slave sockets	*/
    	unsigned int	alen;		/* from-address length		*/
    
    	switch (argc) {
    	case	1:
    		break;
    	case	2:
    		service = argv[1];
    		break;
    	default:
    		errexit("usage: TCPdaytimed [port]
    ");
    	}
    
    	msock = passiveTCP(service, QLEN);
    
    	while (1) {
    		alen = sizeof(fsin);
    		ssock = accept(msock, (struct sockaddr *)&fsin, &alen);
    		if (ssock < 0)
    			errexit("accept failed: %s
    ", strerror(errno));
    		TCPdaytimed(ssock);
    		(void) close(ssock);
    	}
    }
    
    /*------------------------------------------------------------------------
     * TCPdaytimed - do TCP DAYTIME protocol
     *------------------------------------------------------------------------
     */
    void TCPdaytimed(int fd)
    {
    	char	*pts;			/* pointer to time string	*/
    	time_t	now;			/* current time			*/
    	char	*ctime();
    
    	(void) time(&now);
    	pts = ctime(&now);
    	(void) write(fd, pts, strlen(pts));
    }
    
    

    客户端链接服务器时,服务器获取时间然后将获取到的秒数通过 ctime() 函数转换成标准格式返回给客户端。

    Client

    connectsock.c

    /* connectsock.c - connectsock */
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <string.h>
    #include <stdlib.h>
    
    #ifndef	INADDR_NONE
    #define	INADDR_NONE	0xffffffff
    #endif	/* INADDR_NONE */
    
    extern int	errno;
    
    int	errexit(const char *format, ...);
    
    /*------------------------------------------------------------------------
     * connectsock - allocate & connect a socket using TCP or UDP
     *------------------------------------------------------------------------
     */
    int connectsock(const char *host, const char *service, const char *transport )
    /*
     * Arguments:
     *      host      - name of host to which connection is desired
     *      service   - service associated with the desired port
     *      transport - name of transport protocol to use ("tcp" or "udp")
     */
    {
    	struct hostent	*phe;	/* pointer to host information entry	*/
    	struct servent	*pse;	/* pointer to service information entry	*/
    	struct protoent *ppe;	/* pointer to protocol information entry*/
    	struct sockaddr_in sin;	/* an Internet endpoint address		*/
    	int	s, type;	/* socket descriptor and socket type	*/
    
    
    	memset(&sin, 0, sizeof(sin));
    	sin.sin_family = AF_INET;
    
        /* Map service name to port number */
    	if ( pse = getservbyname(service, transport) )
    		sin.sin_port = pse->s_port;
    	else if ((sin.sin_port=htons((unsigned short)atoi(service))) == 0)
    		errexit("can't get "%s" service entry
    ", service);
    
        /* Map host name to IP address, allowing for dotted decimal */
    	if ( phe = gethostbyname(host) )
    		memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
    	else if ( (sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE )
    		errexit("can't get "%s" host entry
    ", host);
    
        /* Map transport protocol name to protocol number */
    	if ( (ppe = getprotobyname(transport)) == 0)
    		errexit("can't get "%s" protocol entry
    ", transport);
    
        /* Use protocol to choose a socket type */
    	if (strcmp(transport, "udp") == 0)
    		type = SOCK_DGRAM;
    	else
    		type = SOCK_STREAM;
    
        /* Allocate a socket */
    	s = socket(PF_INET, type, ppe->p_proto);
    	if (s < 0)
    		errexit("can't create socket: %s
    ", strerror(errno));
    
        /* Connect the socket */
    	if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
    		errexit("can't connect to %s.%s: %s
    ", host, service,
    			strerror(errno));
    	return s;
    }
    
    

    connectTCP.c

    /* connectTCP.c - connectTCP */
    
    int	connectsock(const char *host, const char *service,
    		const char *transport);
    
    /*------------------------------------------------------------------------
     * connectTCP - connect to a specified TCP service on a specified host
     *------------------------------------------------------------------------
     */
    int connectTCP(const char *host, const char *service )
    /*
     * Arguments:
     *      host    - name of host to which connection is desired
     *      service - service associated with the desired port
     */
    {
    	return connectsock( host, service, "tcp");
    }
    
    

    TCPdaytime.c

    /* TCPdaytime.c - TCPdaytime, main */
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #include <stdio.h>
    #include <time.h>
    
    extern int      errno;
    
    int     TCPdaytime(const char *host, const char *service);
    int     errexit(const char *format, ...);
    int     connectTCP(const char *host, const char *service);
    
    #define LINELEN         128
    
    /*------------------------------------------------------------------------
     * main - TCP client for DAYTIME service
     *------------------------------------------------------------------------
     */
    int main(int argc, char *argv[])
    {
            char    *host1 = "localhost";    /* host to use if none supplied */
            char    *host2 = "localhost";    /* host to use if none supplied */
    
            switch (argc) {
            case 1:
                    host1 = "localhost";
                    host2 = "localhost";
                    break;
            case 2:
                    host2 = argv[1];
                    break;
            default:
                    fprintf(stderr, "The number of parameter is to much! Just need the other host~
    ");
                    exit(1);
            }
            TCPdaytime(host1, host2);
            exit(0);
    }
    
    /*------------------------------------------------------------------------
     * TCPdaytime - invoke Daytime on specified host and print results
     *------------------------------------------------------------------------
     */
    int TCPdaytime(const char *host1, const char *host2)
    {
        char    buf[LINELEN+1];         /* buffer for one line of text  */
        int     s1, s2, n;              /* socket, read count           */
        struct  tm time1, time2;
        long    second1, second2;
    
        s1 = connectTCP(host1, "daytime");
        s2 = connectTCP(host2, "daytime");
        
        while( (n = read(s1, buf, LINELEN)) > 0) {
            buf[n] = '';          /* ensure null-terminated       */
            strptime(buf, "%a %b %d %H:%M:%S %Y", &time1);
            printf("time in localhost is	");
            (void) fputs( buf, stdout );
        }
        
        while( (n = read(s2, buf, LINELEN)) > 0) {
            buf[n] = '';          /* ensure null-terminated       */
            strptime(buf, "%a %b %d %H:%M:%S %Y", &time2);
            printf("time in %s is	", host2);
            (void) fputs( buf, stdout ); 
            /*stdout是一个文件指针,C己经在头文件中定义好的了,可以直接使用,把它
              赋给另一个文件指针。只是方便操作输出,比如传给一个函数等等。这时函数
              的输出就不是输出到文件,而是传进来的stdout文件指针,即标准输出。*/
        }
        
        second1 = mktime(&time1);
        second2 = mktime(&time2);
        
        printf("time difference is %d seconds.
    ", abs(second1-second2));
        
        return 0;
    }
    
    

    默认连接的两个主机都是 localhost ,如果输入主机地址,则同时连接 localhost 和目标主机,获取到两台主机的时间。为了比较两台主机之间的时间差,一开始我想着服务器直接返回秒数,但是要转换成字符串才能返回就放弃了。通过 strptime()按照特定时间格式将字符串转换为时间类型,然后通过 mktime()将时间转换为自1970年1月1日以来持续时间的秒数,这样就可以进行运算了。

    编译运行

    编译运行 server:

    将 server 上传到服务器:

    连接服务器开启服务:

    开启客户端,分别只连接 localhost 和同时连接两台服务器:

  • 相关阅读:
    2022北航软件研究生入学考试991考试大纲-数据结构与C
    pgsql 学习
    Java开发必须掌握的 20+ 种 Spring 常用注解
    Spring 学习总结
    Spring MVC快速入门教程
    spring boot与spring mvc的区别是什么?
    Java知识体系最强总结(2020版)
    arthas(阿尔萨斯)使用实践----查看慢方法 /方法耗时等
    JVM --------jmap-----查看堆内存信息、生成heap dump 文件(转储堆内存快照到指定文件)
    [JVM】jstat命令详解---JVM的统计监测工具:jstat 堆内存各部分的使用量,以及加载类的数量。
  • 原文地址:https://www.cnblogs.com/pengzhendong/p/4909906.html
Copyright © 2020-2023  润新知