SOCKET編程-時間服務器和客戶端的實現

UNIX_NET

獲取服務器時間

connect函數

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,
            socklen_t addrlen);

DESCRIPTION
       The  connect()  system  call connects the socket referred to by the file descriptor sockfd to the address specified by addr.  The addrlen argument specifies the size of addr.  The format  of  the address  in  addr is determined by the address space of the socket sockfd; see socket(2) for fur‐
       ther details.

    If the socket sockfd is of type SOCK_DGRAM, then addr is the address to which datagrams are  sent by  default,  and  the  only address from which datagrams are received.  If the socket is of type SOCK_STREAM or SOCK_SEQPACKET, this call attempts to make a connection  to  the  socket  that  is bound to the address specified by addr.

    Generally, connection-based protocol sockets may successfully connect() only once; connectionless protocol sockets may use connect() multiple times to change  their  association.   Connectionless sockets  may  dissolve  the  association by connecting to an address with the sa_family member of sockaddr set to AF_UNSPEC (supported on Linux since kernel 2.2).
    
RETURN VALUE
       If the connection or binding succeeds, zero is returned.  On error, -1 is returned, and errno  is set appropriately.

獲取時間客戶端程序實現

#include "unix_net_public.h"
#include <stdio.h>
#include <strings.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>

#define MAXLINE 4096


int main(int argc, char *argv[])
{
    int sockfd, n;
    char recvline[MAXLINE + 1];
    struct sockaddr_in servaddr;

    //< 沒有指定IP進行報錯處理
    if (argc != 2)
    {
        UNIX_NET_DEBUG("usage: <IPaddress>\n");
        exit(1);
    }
    //< AF_INET 通常代表地址族協議  PF_INET 通產代表網絡協議族協議
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) <  0)
    {
        perror("socket error!\n");
    }

    //<  將地址結構體中的數據進行清空處理
    bzero(&servaddr, sizeof(servaddr));
    
    //< 構造結構體
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(8560); // 沒有找到時間服務器使用ssh端口連接自己的電腦測試連 connect函數的實現


    //< 將點十進制地址轉換爲網絡地址
    if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
    {
        perror("inet_pton error  \n");
    }

    //< 使用connect創建連接
    UNIX_NET_DEBUG("connect start\n");
    if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
    {
        perror("connect error\n");
    }
    UNIX_NET_DEBUG("connect is end !\n");
    //< 接受服務器的回覆數據
    while((n = read(sockfd, recvline, MAXLINE)) > 0)
    {
        recvline[n] = 0;
        if(fputs(recvline, stdout) == EOF)
        {
            perror("fputs error\n");
        }
    }

    if (n < 0)
    {
        perror("error exit()");
    }
    
    
    return 0;
}

支持IPv6的實現

#include "unix_net_public.h"


int
main(int argc, char **argv)
{
	int					sockfd, n;
	struct sockaddr_in6	servaddr;
	char				recvline[MAXLINE + 1];

	if (argc != 2)
		UNIX_NET_DEBUG("usage: a.out <IPaddress>");

    //< 使用網際協議  字節流的形式創建 socket句柄
	if ( (sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
		UNIX_NET_DEBUG("socket error");

	bzero(&servaddr, sizeof(servaddr));

	servaddr.sin6_family = AF_INET6;
	servaddr.sin6_port   = htons(8560);	/* daytime server */
    //< 將IP地址轉換爲網絡地址
	if (inet_pton(AF_INET6, argv[1], &servaddr.sin6_addr) <= 0)
		UNIX_NET_DEBUG("inet_pton error for %s", argv[1]);

	if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
		UNIX_NET_DEBUG("connect error");

    //< 使用阻塞的凡是進行read函數讀取數據
	while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
		recvline[n] = 0;	/* null terminate */
		if (fputs(recvline, stdout) == EOF)
			UNIX_NET_DEBUG("fputs error");
	}
	if (n < 0)
		UNIX_NET_DEBUG("read error");

	return 0;
}

獲取時間服務器實現

#include "unix_net_public.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;
    //< 監聽所有的笛子, Address to accept any incoming messages. 
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    //< 儘量使用大於8000的端口,小端口在linux上使用可能會報錯不讓使用
	servaddr.sin_port        = htons(8560);	/* daytime server */

    //< 綁定套接字
	bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));

	listen(listenfd, LISTENQ);

	for ( ; ; ) {
		connfd = accept(listenfd, (struct sockaddr *) NULL, NULL);

        ticks = time(NULL);
        snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
        write(connfd, buff, strlen(buff));

		close(connfd);
	}
}

以上代碼全部在UBUNTU 16.04上測試通過,可以去github上下載對應的源碼,直接編譯

測試

$ ./putdaytimesrv &
[1] 2952
$ ./getdaytimetcpclient 127.0.0.1
[getdaytimetcpclient.c], lien  = [47]connect start
[getdaytimetcpclient.c], lien  = [52]connect is end !
Sun Jul 14 14:57:22 2019

數據發送的過程剛好是三次握手,四次揮手加上一次(PSH,ACK)

抓包分析:
啓動wireshark 抓取lo網卡的網絡數據:
然後執行上述操作,對操作過程中產生的網絡數據進行抓包
整個過程經歷了三次握手和四次揮手,途中總共有7幀數據,前三幀是三次握手,後四幀是四次揮手,中間的那一次是PSH, ACK幀,是數據的發送幀
在這裏插入圖片描述



小技巧

宏定義的使用&&變參函數的宏定義實現

#define UNIX_NET_DEBUG(...) do { \
                    printf("[%s], lien  = [%d]", __FILE__, __LINE__); \
                    printf(__VA_ARGS__); \
                     }while(0)

源碼地址

github

關注公衆號

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章