• UNIX网络编程卷1 时间获取程序server TCP 协议相关性


    本文为senlie原创。转载请保留此地址:http://blog.csdn.net/zhengsenlie


    最初代码: 
    这是一个简单的时间获取server程序。它和时间获取程序client一道工作。


     它是 协议相关,把代码中出现的左边的字符串换为右边的,就变成了IPv6版本号的
     IPv4 --> IPv6
     sockaddr_in --> sockaddr_in6
     AF_INET --> AF_INET6
     sin_family --> sin6_family
     sin_port --> sin6_port
     sin_addr --> sin6_addr

    #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;
    
    
    	//1.创建 TCP 套接字  --> 为什么不检查一下创建成功还是失败呢 ? 
    	// --> 大写字母开头的函数,如 Socket 是包裹函数,里面已经进行了错误处理
    	listenfd = Socket(AF_INET, SOCK_STREAM, 0);
    
    
    	//2.把server的众所周知的port捆绑到套接字
    	//使用 bzero 把套接字地址结构 servaddr 清零
        //设置地址族为 AF_INET
    	//设置 IP 地址为 INADDR_ANY 。假设server主机骨多个网络接口。server进程就能够在随意网络接口上接受客户连接
        //设置port号为 13
    	bzero(&servaddr, sizeof(servaddr));  
    	servaddr.sin_family      = AF_INET; 
    	servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    	servaddr.sin_port        = htons(13);	 
    	//绑定套接字
    	Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
    
    
    	//3.把套接字转换为监听套接字
    	//这样才干够接受来自客户的外来连接
    	//LISTENQ 指定最大客户连接数
    	Listen(listenfd, LISTENQ);
    
    
    	//4.接受客户连接,发送应答
    	for ( ; ; ) {
    		//server进程在 accept 调用中被挂起,等某个客户连接的到达。完毕 TCP 三次握手后 accept 才返回
    		connfd = Accept(listenfd, (SA *) NULL, NULL); //已连接描写叙述符
    
    
            ticks = time(NULL);
            snprintf(buff, sizeof(buff), "%.24s
    ", ctime(&ticks));
            Write(connfd, buff, strlen(buff)); //把当前时间写给客户
            
    		//5.终止连接
    		// close 调用引发 TCP 连接终止序列。总共发送四个 TCP 分组。
    		// 每一个方向一个 Fin,每一个 Fin 又由各自的对端确认
    		Close(connfd);
    	}
    }
    


    问题1:协议相关
    改善:使用 getaddrinfo 和 tcp_listen 来同一时候支持 IPv4 和 IPv6
    /**
    * TCP 协议无关。调用 getaddrinfo 和 tcp_listen
    **/
    #include	"unp.h"
    
    
    int
    tcp_listen(const char *host, const char *serv, socklen_t *addrlenp)
    {
    	int				listenfd, n;
    	const int		on = 1;
    	struct addrinfo	hints, *res, *ressave;
    
    
    	//1.调用 getaddrinfo 
    	//协议地址话为 AF_UNSPEC ,套接字类型为 SOCK_STREAM
    	//由于本函数供server使用,所以还要加一个 AI_PASSIVE 的标志
    	bzero(&hints, sizeof(struct addrinfo));
    	hints.ai_flags = AI_PASSIVE;
    	hints.ai_family = AF_UNSPEC;
    	hints.ai_socktype = SOCK_STREAM;
    
    
    	if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
    		err_quit("tcp_listen error for %s, %s: %s",
    				 host, serv, gai_strerror(n));
    	ressave = res;
    
    
    	//2.尝试每一个 addrinfo 结构直到成功或到达链表尾
    	do {
    		//创建套接字
    		listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    		if (listenfd < 0)
    			continue;		/* error, try next one */
    
    
    		//设置套接字选项
    		Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    		
    		//绑定套接字
    		if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
    			break;			//成功
    		
    		//绑定失败,尝试下一个
    		Close(listenfd);	
    	} while ( (res = res->ai_next) != NULL);
    
    
    	//3.检查是否失败
    	if (res == NULL)	//socket() 或 bind() 得到的 errorno 
    		err_sys("tcp_listen error for %s, %s", host, serv);
    
    
    	//4.把套接字转换为监听套接字
    	Listen(listenfd, LISTENQ);
    
    
    	if (addrlenp)
    		*addrlenp = res->ai_addrlen;	/* return size of protocol address */
    
    
    	//5.调用 freeaddrinfo 清理由 getaddrinfo 返回的动态存储空间
    	freeaddrinfo(ressave);
    
    
    	//6.返回建立的监听套接字
    	return(listenfd);
    }
    /* end tcp_listen */
    
    
    int
    Tcp_listen(const char *host, const char *serv, socklen_t *addrlenp)
    {
    	return(tcp_listen(host, serv, addrlenp));
    }
    
    
    /**
    * TCP 协议无关,调用 getaddrinfo 和 tcp_listen
    **/
    #include	"unp.h"
    #include	<time.h>
    
    
    int
    main(int argc, char **argv)
    {
    	int				listenfd, connfd;
    	socklen_t		len;
    	char			buff[MAXLINE];
    	time_t			ticks;
    	struct sockaddr_storage	cliaddr;
    
    
    	//1.利用 Tcp_listen 得到监听套接字
    	if (argc == 2)
    		listenfd = Tcp_listen(NULL, argv[1], &addrlen);
    	else if (argc == 3)
    		listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
    	else
    		err_quit("usage: daytimetcpsrv1 [ <host> ] <service or port>");
    	
    
    
    	//2.server循环。

    接受客户连接。发送应答 for ( ; ; ) { len = sizeof(cliaddr); //server堵塞在 accept 调用。等待客户连接 connfd = Accept(listenfd, (SA *)&cliaddr, &len); printf("connection from %s ", Sock_ntop((SA *)&cliaddr, len)); //发送应答 ticks = time(NULL); snprintf(buff, sizeof(buff), "%.24s ", ctime(&ticks)); Write(connfd, buff, strlen(buff)); //关闭已连接套接字 Close(connfd); } }



    问题2:一次仅仅能处理一个客户
    改善:大多数 UDP server是迭代的。一次仅仅处理一个客户。大多数 TCP server是并发的。


    并发最简单的技术是调用 fork 函数为每一个客户创建一个子进程。其它技术包含使用线程
    取代 fork,或在server启动时预先 fork 一定数量的子进程
    这部分的内容在我的其它博文 回射server程序 和 server程序设计范式 里有对应的代码演示样例


    问题3:长时间执行
    改善:daumon  [todo]



  • 相关阅读:
    ubuntu防火墙设置通过某端口
    pandas入门
    pyplot入门
    numpy教程
    跨域请求 spring boot
    spring boot 启动流程
    代理配置访问
    AOP,拦截器
    spring boot 启动不连接数据库
    Python 3.x 连接数据库(pymysql 方式)
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/5096845.html
Copyright © 2020-2023  润新知