1. 程式人生 > >[Linux] c 語言tcp socket 示例從簡單到複雜

[Linux] c 語言tcp socket 示例從簡單到複雜

服務端:

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

#define MAXLINE 80
#define SERV_PORT 8000

int main(void)
{
    struct sockaddr_in servaddr, cliaddr;
    socklen_t cliaddr_len;
    int listenfd, connfd;
    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];
    int i, n;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    listen(listenfd, 20);

    printf("Accepting connections ...\n");
    while(1) {
        cliaddr_len = sizeof(cliaddr);
        connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
        n = read(connfd, buf, MAXLINE);
        printf("received from %s at PORT %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port));
        for(i = 0; i < n; i++)
            buf[i] = toupper(buf[i]);
        write(connfd, buf, n);
        close(connfd);
    }
}

客戶端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define MAXLINE 80
#define SERV_PORT 8000

int main(int argc, char *argv[])
{
    struct sockaddr_in servaddr;
    char buf[MAXLINE];

    int sockfd, n;
    char *str;

    if(argc != 2) {
        fputs("usage: ./client message\n", stderr);
        exit(1);
    }
    
    str = argv[1];

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
    servaddr.sin_port = htons(SERV_PORT);
    
    connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
   
    write(sockfd, str, strlen(str));

    n = read(sockfd, buf, MAXLINE);
    printf("Response from server:\n");
    write(STDOUT_FILENO, buf, n);

    close(sockfd);
    return 0;
}

上面例子是最基本的tcp socket 流程,沒有錯誤處理,下面寫一個模組 wrap.c 給socket函式加上錯誤處理程式碼包裝成新的函式;

服務端也不能接收多個客戶端請求,使用fork()函式建立一個子程序專門服務客戶端,而父程序專門負責監聽埠,再使用sigaction()函式處理SIGCHLD訊號呼叫wait清理殭屍程序;使用setsockopt設定socket描述符選項SO_REUSEADDR為1,允許建立埠號相同但ip地址不同的多個socket描述符。h

客戶端輸入也不是互動式的,將其改為互動式的,並且處理伺服器端關閉連線後客戶端繼續寫資料產生的SIFPIPE訊號(列印“*******”,未做實際處理)。

wrap.h

#ifndef WRAP_H
#define WRAP_H

void perr_exit(const char *s);
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);
void Bind(int fd, const struct sockaddr *sa, socklen_t salen);
void Connect(int fd, struct sockaddr *sa, socklen_t salen);
void Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void *ptr, size_t nbytes);
ssize_t Write(int fd, void *ptr, size_t nbytes);
ssize_t Readn(int fd, void *vptr, size_t n);
ssize_t Writen(int fd, const void *vptr, size_t n);
ssize_t Readline(int fd, void *vptr, size_t maxlen);
void Close(int fd);

#endif


wrap.c
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>

void perr_exit(const char *s)
{
    perror(s);
    exit(1);
}

int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr) 
{
    int n;
again:
    if ((n = accept(fd, sa, salenptr)) < 0) {
        if ((errno == ECONNABORTED) || (errno == EINTR)) {
            goto again;         
        } else {
            perr_exit("accept error");
        }
    }
    return n;
}

void Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
    if(bind(fd, sa, salen) < 0){
        perr_exit("bind error");
    }
}

void Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{
    if (connect(fd, sa, salen) < 0) {
        perr_exit("connect error");
    }
}

void Listen(int fd, int backlog)
{
    if(listen(fd, backlog) < 0){
        perr_exit("listen error");
    }
}

int Socket(int family, int type, int protocol)
{
    int n;
    if ((n = socket(family, type, protocol)) < 0) {
        perr_exit("socket error");
    }
    return n;
}

ssize_t Read(int fd, void *ptr, size_t nbytes)
{
    ssize_t n;
again:
    if((n = read(fd, ptr, nbytes)) == -1) {
        if(errno == EINTR) {
            goto again;
        } else {
            return -1;
        }
    }
    return n;
}

ssize_t Write(int fd, const void *ptr, size_t nbytes) 
{
    ssize_t n;
again:
    if ((n = write(fd, ptr, nbytes)) == -1) {
        if (errno == EINTR) {
            goto again;
        } else {
            return -1;
        }
    }
    return n;
}

ssize_t Readn(int fd, void *vptr, size_t n)
{
    size_t nleft;
    ssize_t nread;
    char *ptr;

    ptr = vptr;
    nleft = n;
    while(nleft > 0) {
        if( (nread = read(fd, ptr, nleft)) < 0) {
            if( errno == EINTR ) {
                nread = 0;
            }else if( nread == 0 ) {
                break;
            }
        }

        nleft -= nread;
        ptr += nread;
    }
    return n - nleft;
}

ssize_t Writen(int fd, const void *vptr, size_t n)
{
    size_t nleft;
    ssize_t nwrittrn;
    const char *ptr;

    ptr = vptr;
    nleft = n;

    while(nleft > 0) {
        if (( nwrittrn = write(fd, ptr, nleft)) <= 0) {
            if (nwrittrn  < 0 && errno == EINTR) {
                nwrittrn = 0;
            } else {
                return -1;
            }
        }

        nleft -= nwrittrn;
        ptr += nwrittrn;
    }
    return n;
}

static ssize_t my_read(int fd, char *ptr) {
    static int read_cnt;
    static char *read_ptr;
    static char read_buf[100];

    if (read_cnt <= 0) {
    again:
        if((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
            if (errno == EINTR) {
                goto again;
            }
            return -1;
        } else if(read_cnt == 0) {
            return 0;
        }
        read_ptr = read_buf;
    }
    read_cnt --;
    *ptr = *read_ptr++;
    return 1;
}


ssize_t Readline(int fd, void *vptr, size_t maxlen) 
{
    ssize_t n, rc;
    char c, *ptr;

    ptr = vptr;
    for (n = 0; n < maxlen; n++) {
        rc = my_read(fd, &c);
        if (rc == 1) {
            *ptr++ = c;
            if (c == '\n') {
                break;
            }       
        }else if(rc == 0) {
            *ptr = 0;
            return n-1;
        }else {
            return -1;
        }
    }
    
    *ptr = 0;
    return n;
}

void Close(int fd)
{
    if(close(fd) == -1) {
        perr_exit("close error");
    }
}


server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <signal.h>
#include <sys/wait.h>

#include "wrap.h"


#define MAXLINE 80
#define SERV_PORT 8000

void handle_sig(int sig) 
{
    printf("child exit\n");
    wait(NULL);
}


int main(void)
{
    struct sockaddr_in servaddr, cliaddr;
    socklen_t cliaddr_len;
    int listenfd, connfd;
    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];
    int i, n;

    
    struct sigaction act;
    act.sa_handler = handle_sig;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGCHLD,&act,NULL);


    listenfd = Socket(AF_INET, SOCK_STREAM, 0);
    
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    Listen(listenfd, 20);

    printf("Accepting connections ...\n");
    while(1) {
        cliaddr_len = sizeof(cliaddr);
        connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
       
        n = fork();
        if ( n == -1) {
            perror("call to fork");
            exit(1);
        } else if (n == 0) { // child
            Close(listenfd);

             while(1) {
                n = Read(connfd, buf, MAXLINE);
                if (n == 0) {
                    printf("the other side has been closed.\n");
                    break;
                }
                printf("received from %s at PORT %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port));
                for(i = 0; i < n; i++)
                    buf[i] = toupper(buf[i]);
                Write(connfd, buf, n);
            }
            Close(connfd);
            exit(0);
       } else {  // parent
            Close(connfd);
       }
    }
}

client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>

#include "wrap.h"

#define MAXLINE 80
#define SERV_PORT 8000

void handle_sig(int signle) 
{
    printf("********\n");
}

int main(int argc, char *argv[])
{
    struct sockaddr_in servaddr;
    char buf[MAXLINE];
    int sockfd, n;

    struct sigaction act;
    act.sa_handler = handle_sig;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGPIPE,&act,NULL);

    sockfd = Socket(AF_INET, SOCK_STREAM, 0);
    
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
    servaddr.sin_port = htons(SERV_PORT);
    
    Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    while(fgets(buf, MAXLINE, stdin) != NULL) {
         Write(sockfd, buf, strlen(buf));
         n = Read(sockfd, buf, MAXLINE);
         if ( n==0 ) {
            printf("the other side has been closed.\n");
         }else {
            Write(STDOUT_FILENO, buf, n);
         }
    }

    Close(sockfd);
    return 0;
}

執行:

gcc server.c wrap.c -o server

gcc client.c wrap.c -o client 

./server

./client