1. 程式人生 > 其它 >UNPv13:#第5章#TCP客戶/伺服器程式示例

UNPv13:#第5章#TCP客戶/伺服器程式示例

Code

github

//server.c

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

void sig_chld(int signo)
{
    pid_t pid;
    int stat;
    while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
        printf("child %d terminatedn", pid);

    return;
}

int main(int argc, char* argv[])
{
    int listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(listenfd == -1)
    {
        perror("function socket error :");
        exit(-1);
    }

    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(atoi(argv[1]));
    if(bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)
    {
        perror("function bind error : ");
        exit(-1);
    }

    if(listen(listenfd, 5) == -1)
    {
        perror("function listen error : ");
        exit(-1);
    }

    struct sigaction act, oact;
    act.sa_handler = sig_chld;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if(sigaction(SIGCHLD, &act, &oact) == -1)
    {
        perror("function sigaction error : ");
        exit(-1);
    }

    int connfd;
    struct sockaddr_in clientaddr;
    socklen_t clientlen;
    while(1)
    {
        clientlen = sizeof(clientaddr);
        connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientlen);
        if(connfd == -1)
        {
            if(errno == EINTR)
                continue;
            perror("function accept error : ");
            exit(-1);
        }

        if(fork() == 0)
        {
            if(close(listenfd) == -1)
            {
                perror("function close error : ");
                exit(-1);
            }

            char buf[1024];
            int len;
        AGAIN:
            while((len = read(connfd, buf, 1024)) > 0)
                write(connfd, buf, len);
            if(len < 0 && errno == EINTR)
                goto AGAIN;
            else if(len < 0)
            {
                perror("function read error : ");
                exit(1);
            }
        }
        else
        {
            if(close(connfd) == -1)
            {
                perror("function close error : ");
                exit(-1);
            }
        }
    }
    return 0;
}
//client.c

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

int main(int argc, char* argv[])
{
    int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sockfd == -1)
    {
        perror("function socket error : ");
        exit(-1);
    }

    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    switch(inet_pton(AF_INET, argv[1], &serveraddr.sin_addr))
    {
    case 0:
        perror("function inet_pton error (input invalid) : ");
        exit(-1);
        break;
    case 1:
        //successed
        break;
    default:
        perror("function inet_pton error : ");
        exit(-1);
        break;
    }
    serveraddr.sin_port = htons(atoi(argv[2]));
    connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));

    char buf[1024];
    while(fgets(buf, 1024, stdin) != NULL)
    {
        write(sockfd, buf, strlen(buf));
        if(read(sockfd, buf, 1024) == 0)
        {
            perror("server terminated");
            exit(0);
        }
        fputs(buf, stdout);
    }

    return 0;
}

伺服器程序終止

客戶TCP收到FIN只是表示伺服器程序已關閉連線的服務端,從而不再往其中傳送任何資料而已。FIN的接收並沒有告知客戶TCP伺服器程序已經終止。

SIGPIPE訊號

當一個程序向某個已收到RST的套接字執行寫操作時,核心向該程序傳送一個SIGPIPE訊號。該訊號預設行為是終止程序。不論該程序捕獲該訊號並從其訊號處理函式返回,還是簡單的忽略該訊號,寫操作都將返回EPIPE。 第一次寫操作引發RST,第二次寫引發SIGPIPE訊號。寫一個已接收FIN的套接字不成問題,但寫一個已接收了RST的套接字則是一個錯誤。