网络编程---TCP客户/服务器编程笔记

       在通过多进程构建并发服务器时,重要的一点就是父进程对子进程的回收,以节省资源。在一个子进程结束时,内核会产生SIGCHLD信号通知父进程,父进程要捕获该信号,并调用信号处理函数回收子进程的资源,这里用到了signal函数和waitpid函数,waitpid函数和wait函数的最大区别就是waithpid函数可以使父进程处于非阻塞状态,即如果没有子进程结束,waitpid函数可以不阻塞父进程。

        accept函数会阻塞父进程,它是慢系统调用,当阻塞于某个慢系统调用(accept)的一个进程捕获某个信号且相应信号处理函数返回时,该系统调用(accept)可能返回一个EINTR错误,有些内核自动重启某些被中断的系统调用,但是为了便于移植,我们最好自己编写重启系统调用。

server.h

#ifndef SERVER_H
#define SERVER_H

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>

#define PORT 3333



void str_echo(int sockfd);


#endif // SERVER_H

server.c

#include "server.h"
#define MAXLINE 1024

void sig_child(int signo);

int main(void)
{
    pid_t pid;
    int listenfd, connfd;
    struct sockaddr_in serveraddr, clientaddr;
    socklen_t clientlen = 0;

    if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
    {
        perror("socket error");
        exit(1);
    }

    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(PORT);
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if( bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0 )
    {
        perror("socket error");
        exit(1);
    }

    if( listen(listenfd, 5) < 0 )
    {
        perror("socket error");
        exit(1);
    }

    /*捕获SIGCHLD信号,回收子进程*/
    signal(SIGCHLD, sig_child);

    for( ; ; )
    {
        clientlen = sizeof(clientaddr);

        if( (connfd = accept(listenfd, (struct sockaddr*)&clientaddr, &clientlen)) < 0 )
        {
            /*如果是中断错误,要重启accept函数*/
            if(errno == EINTR)
                continue;
            else
            {
                perror("socket error");
                exit(1);
            }
        }

        if( (pid = fork()) < 0 )
        {
            perror("socket error");
            exit(1);
        }
        else if(pid == 0)  /*创建子进程执行处理程序*/
        {
            close(listenfd);  /*关闭监听套接字描述符*/
            str_echo(connfd);
            exit(0);   /*子进程退出时关闭所有的套接字描述符*/
        }

        close(connfd);  /*父进程关闭内核新产生的用于连接的套接字*/
    }

    exit(0);
}

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

    while( (pid = waitpid(-1, &stat, WNOHANG)) > 0 ) /*非阻塞的捕获结束的子进程*/
        printf("child %d terminated\n", pid);

    return;
}

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

    while(1)
    {
        if( (n = read(sockfd, buf, MAXLINE)) < 0 )
        {
            if(errno == EINTR)
                continue;
            else
            {
                perror("str_echo: read error");
                exit(1);
            }
        }
        if(n == 0)
            break;

        if( write(sockfd, buf, n) != n )
        {
            perror("str_echo: write error");
            exit(1);
        }
    }
}

client.h

#ifndef CLIENT_H
#define CLIENT_H

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <arpa/inet.h>

#define PORT 3333

void str_cli(FILE *, int sockfd);

#endif // CLIENT_H

 

client.c

#include "client.h"
#define MAXLINE 1024

int main(int argc, char **argv)
{
    int i, sockfd[5];
    struct sockaddr_in serveraddr;

    if(argc != 2)
    {
        perror("usage: client");
        exit(1);
    }

    for(i=0; i < 5; i++)
    {
        if( (sockfd[i] = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
            perror("socket error");
            exit(1);
        }

        bzero(&serveraddr, sizeof(serveraddr));
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_port = htons(PORT);
        inet_pton(AF_INET, argv[1], &serveraddr.sin_addr);

        if( connect(sockfd[i], (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0 )
        {
            perror("connect error");
            exit(1);
        }
    }

    str_cli(stdin, sockfd[0]);

    exit(0);
}

void str_cli(FILE *fp, int sockfd)
{
    int n;
    char sendline[MAXLINE], recvline[MAXLINE];

    while( fgets(sendline, MAXLINE, fp) != NULL )
    {
        if( write(sockfd, sendline, strlen(sendline)) != strlen(sendline) )
        {
            perror("str_cli: write error");
            exit(1);
        }

        if( (n = read(sockfd, recvline, MAXLINE)) < 0 )
        {
            perror("str_cli: read error");
            exit(1);
        }
        recvline[n] = '\0';
        fputs(recvline, stdout);
    }
}


 


 


 


 

 

你可能感兴趣的:(网络编程---TCP客户/服务器编程笔记)