UNP - 第五章 TCP客户/服务器示例 - 学习笔记

1 - 前言

本章在第四章的理论知识基础上,实现一个TCP/IP客户服务器的原型程序。这个原型程序是执行以下步骤的一个回射服务器。
(1)客户从标准输入读入一行文本,并写给服务器
(2)服务器从网络输入读取这行文本,并回射给客户
(3)客户从网络输入读入这行回射文本,并显示在标注输入上

该程序的模型图:
这里写图片描述

该程序时后续我们研究套接字通信的基础模型,其他的比如I/O复用,多进程服务器,多线程服务器等均基于该程序作了相应的改动。所以十分重要。

除了这个基础的程序之外,还会分析多种特殊边界情况。例如:
1,客户和服务器启动时发生什么?
2,客户正常终止时发生什么?
3,若服务器进程在客户之前终止,则客户发生什么?
4,若服务器主机崩溃,则客户发生什么?
通过这些情况的分析,弄清楚网络层次发生了什么,以及对应套接字的API是如何处理的?

2 - tcp回射服务器程序

#include "unp.h"
#include "apue.h"


void str_echo(int sockfd){
    ssize_t  n;
    char    buf[MAXLINE];

again:
    while((n == read(sockfd,buf,MAXLINE)) > 0){
        write(sockfd,buf,n);
    }
    if(n < 0 && errno == EINTR){
        goto again;
    }
    else if(n < 0)
        err_sys("str_echo : read error");

}

int main(int argc,char **argv){
    int     listenfd,connfd;
    pid_t   childpid;
    socklen_t   clilen;
    struct sockaddr_in cliaddr,servaddr;

    if((listenfd = socket(AF_INET,SOCK_STREAM,0) )< 0){
        err_quit("socket create error");
    }

    bzero(&servaddr,sizeof(servaddr));

    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    if(bind(listenfd,(SA *)&servaddr,sizeof(servaddr)) < 0){
        err_quit("bind error");
    }

    if(listen(listenfd,LISTENQ) < 0){
        err_quit("listen error");
    }

    for(;;){
        clilen = sizeof(cliaddr);
        if((connfd = accept(listenfd,(SA* )&cliaddr,&clilen)) < 0){
            err_sys("accept error");
        }else{
            if((childpid = fork()) == 0){     //child process
                close(listenfd);    //close listening socket
                str_echo(connfd);   //process the request 
                exit(0);
            }
            close(connfd);  //parent close conneted socket
        }

    }
}

分析:
服务器进程,首先创建套接字,然后绑定端口号和地址,然后调用listen系统调用,进入监听状态。然后调用accept系统调用,等待已完成的tcp连接。
在str_echo回射处理函数中,调用read系统调用从网络输入中读取数据。然后在调用write函数,将其发送给客户端。

3 - TCP回射客户程序

4 - 几种边界情况分析

4 - 1 客户和服务器启动时发生什么? ?

对于服务器来说,启动服务器后,它调用socket,bind,listen和accept,并阻塞于accept调用。在启动客户端之前,服务器套接字处于LISTEN状态。

当客户端调用socket和connect,后者引起tcp的三次握手过程。当三次握手过程完成后,客户中的connect和服务器的accept均返回、连接建立。此时双方的套接字均为established状态。

接这发生如下步骤:
1,客户调用str_cli函数,该函数阻塞于fgets调用。直到输入内容
2,当服务器中的accept返回时,服务器调用fork,在由子进程调用str_echo,该函数调用readline,readline调用read。而read在等待客户送入一行文本期间阻塞。
3,服务器父进程再次调用accept并阻塞。等待下一个客户连接。

4 - 2 客户正常终止时发生什么?

正常情况的步骤如下:
1,当我们键入EOF字符时,fgets返回以空指针,于是str_cli函数返回
2,当str_cli返回到客户的main函数时,main通过调用exit终止。
3,进程终止处理的部分工作是关闭所有打开的描述符,因此客户打开的套接字有内核关闭。这导致客户tcp发送一个FIN给服务器。服务器TCP则以ACK响应。这就是TCP连接终止序列的前半部分。支持,服务器套接字处于CLOSE_WAIT状态。客户套接字处于FIN_WAIT_2状态。
4,当服务器TCP接收FIN,服务器子进程阻塞于readline调用,于是readline返回0。这导致str_echo 函数返回服务器子进程的main函数。
5,服务器子进程通过调用exit来终止。
6,服务器子进程中国开发的所有描述符随之关闭。由子进程来关闭已经连接套接字会引起终止序列的最后两个分节:一个从服务器的FIN和一个从客户到服务器的ACK。至此,连接完全终止,客户套接字进入TIME_WAIT状态。
7,进程终止的另一部分内容是:在服务器进程终止时,给父进程一个SIGCHLD信号。在本例中,该信号产生了,但是我们在代码并没有捕捉该信号。而该信号的默认行为时被忽略。既然父进程未处理。则子进程处于僵尸状态。

4 - 3 若服务器进程在客户之前终止,则客户发生什么?

4 - 4 若服务器主机崩溃,则客户发生什么?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值