TCP客户/服务器程序示例

本文使用基本的函数编写一个完整的TCP客户/服务器程序示例。这个简单的例子是执行如下步骤的一个回射服务器:

  1. 客户从标准输入读入一行文本,并写给服务器。
  2. 服务器从网络输入读入这行文本,并回射给用户。
  3. 客户从网络输入读入这行回射文本,并显示在标准输出上。
int main(int argc,char **argv){
    int listenfd,connfd;
    pid_t childpid;
    socklen_t clilen;
    struct sockaddr_in cliaddr,servaddr;
    
    listenfd = socket(AF_INET,SOCK_STREAM,0);
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
    listen(listenfd,LISTENQ);
    for( ; ; ){
        clilen = sizeof(cliaddr);
        connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&clilen);
        if((childpid = fork())==0){
            close(listenfd);
            str_echo(connfd);
            exit(0);
        }
        close(connfd);
    }
    return 0;
}

其中,INADDR_ANY表示的是通配地址,SERV_PORT在头文件unp.h中定义为9877

 

TCP回射服务器程序:str_echo函数

str_echo函数执行处理每个客户的服务:从客户读入数据,并把它们回射给客户。

void str_echo(int sockfd){

    ssize_t n;
    char buf[MAXSIZE];

    again:
        while((n = read(sockfd,buf,MAXLINE))>0)
            writen(sockfd,buf,n);

        if(n<0 && errno == EINTR)
            goto again;

        else if(n<0)
            err_sys("str_echo:read error");
}

 

TCP回射客户程序:main函数

int main(int argc,char **argv){
    int sockfd,
    struct sockaddr_in servaddr;
    if(argc!=2)
        err_quit("usage:tcpcli<IPaddress>");
    sockid = socket(AF_INET,SOCK_STREAM.0);
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET,argv[1],&servaddr.sin_addr);
    
    connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
    
    str_cli(stdin,sockfd);

    exit(0);

}

 

TCP回射客户程序:str_cli函数

str_cli函数完成客户处理循环:从标准输入读入一行文本,写到服务器上,都会服务器对该行的回射,并把回射行写到标准输出上。

void str_cli(FILE *fp,int sockfd){

    char sendline[MAXLINE],recvline[MAXLINE];
    while(fgets(sendline,MAXLINE,fp)!=NULL){
        writen(sockfd,sendline,strlen(sendline));

        if(readline(sockfd,recvline,MAXLINE)==0)
            err_quit("str_cli:server terminated prematurely");

        fputs(recvline,stdout);
    }
}

 

POSIX信号处理

信号(signal)就是告知某个进程发生了某个时间的通知,有时也称为软件中断。信号通常是异步发生的,也就是说进程预先不知道信号的准确发生时刻。

信号可以:

  • 由一个进程发给另一个进程(或自身)。
  • 由内核发给某个进程。

每个信号都有一个与之关联的处置(disposition),也称为行为(action)。我们通过调用sigaction函数来设定一个信号的设置,并有三种选择:

  1. 提供一个函数,只要有特定信号发生它就被调用。这样的函数称为信号处理函数,这种行为称为捕获信号。有两个信号不能被捕获,他们是
    1. SIGKILL
    2. SIGSTOP
    3. 且该函数的原型是:void handler(int signo);
  2. 可以把某个信号的处置设定为SIG_IGN来忽略它。SIGKILL和SIGSTOP这两个信号不能被忽略。
  3. 可以把某个信号的处置设定为SIG_DFL来启用它的默认处置。默认处置通常在收到信号后终止进程,其中某些信号还在当前工作目录产生一个进程的核心映像。

 

signal函数

建立信号处置的POSIX方法就是调用signaction函数。不过这有点复杂,所以一般简单些的方法就是调用signal函数,其第一个参数是信号名,第二个参数是指向函数的指针,或为常值SIG_IGN或SIG_DFL。

void (*signal(int signo,void (*func)(int)))(int)

处理僵死进程

僵死进程占用内核中的空间,最终可能导致耗尽进程资源。无论何时我们fork子进程都得wait它们,以防它们变成僵死进程。

void sig_chld(int signo){
    pid_t pid;
    int stat;

    pid = wait(&stat);
    printf("child %d terminated\n",pid);
    return;
}

 

wait和waitpid函数

#include<sys/wait.h>

//均返回:若成果返回进程ID,若出错返回0或-1
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid,int *statloc,int options);

函数wait和waitpid均返回两个值:已终止子进程的进程ID号,以及通过statloc指针返回的子进程终止状态(一个整数)。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值