linux select()詳解( 三)-- TCP最簡實例

通過本文你會了解到:
1. TCP server的實例
2. TCP client的實例
3. TCP server和client的運行測試
4. TCP C/S模型的思考

約定
1. 格式爲 /**/ 的註釋對程序的主要流程進行說明
2. 格式爲 // 的註釋對程序的難懂語句進行說明

TCP server實例(server.c)

#include <stdio.h>      /* for printf() and fprintf() */
#include <sys/types.h>  /* for Socket data types */
#include <sys/socket.h> /* for socket(), connect(), send(), and recv() */
#include <netinet/in.h> /* for IP Socket data types */
#include <arpa/inet.h>  /* for sockaddr_in and inet_addr() */
#include <stdlib.h>     /* for exit() */
#include <string.h>     /* for memset() */
#include <unistd.h>     /* for close() */
#include <errno.h>      /* for errno*/

//macro
#define BUF_SIZE 1024 
#define PORT 1025 
#undef max
#define max(x, y) ((x) > (y) ? (x) : (y))
#undef handle_error
#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while(0)

int main(int argc, char *argv[])
{
    struct sockaddr_in server_addr;
    struct sockaddr_in conn_addr;
    fd_set read_fds;
    socklen_t len;
    char buf[BUF_SIZE];
    int conn_fd = -1;
    int server_fd = -1;
    int ret;

    /*創建tcp描述符*/
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(server_fd == -1)
        handle_error("socket");

    /*指定server信息並綁定*/
    memset(&server_addr, 0, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //ip地址爲任意,只要port滿足即可

    if(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
        handle_error("bind");

    /*監聽server描述符*/
    if(listen(server_fd, 5) == -1)
        handle_error("listen");

    printf("accepting connections on port %d\n", PORT);
    while(1) {
        int max_fd = 0;

        FD_ZERO(&read_fds);
        FD_SET(server_fd ,&read_fds);
        max_fd = max(server_fd, max_fd);
        if(conn_fd > 0) {
            FD_SET(conn_fd ,&read_fds);
            max_fd = max(conn_fd, max_fd);
        }
        /*檢測描述符集中的描述符的I/O狀態*/
        ret = select(max_fd + 1, &read_fds, NULL, NULL, NULL);

        if(ret == -1 && errno == EINTR)
            continue;

        if(ret == -1)
            handle_error("select");

        if(FD_ISSET(server_fd, &read_fds)) { //server_fd可讀,即有client連接
            len = sizeof(conn_addr);
            if(conn_fd > 0) { //如果已有連接,則先關閉,因此此server只與最後一次連接的client進行通信
                close(conn_fd);
            }
            /*接受並得到client信息*/
            conn_fd = accept(server_fd, (struct sockaddr *)&conn_addr, &len);
            if(conn_fd == -1)
                handle_error("accept");
            printf("client is connected\n");

        } else if(FD_ISSET(conn_fd, &read_fds)) { //conn_fd可讀,即接收到client數據
            int n;
            memset(buf, 0, BUF_SIZE);
            n = recv(conn_fd, buf, BUF_SIZE, 0);
            if(n == -1) { //recv函數返回錯誤
                perror("recv");
                close(conn_fd);
                conn_fd = -1;
            } else if(n == 0) { //client端套接字被關閉
                printf("client is closed\n");
                close(conn_fd);
                conn_fd = -1;
            } else { //讀取成功
                /*對收到的數據進行處理*/
                printf("receive data:%s\n", buf);
            }
        }
    }

    /*關閉描述符*/
    close(server_fd);

    return 0;
}

TCP client實例(client.c)

#include <stdio.h>      /* for printf() */
#include <sys/types.h>  /* for Socket data types */
#include <sys/socket.h> /* for socket(), connect(), send(), and recv() */
#include <netinet/in.h> /* for IP Socket data types */
#include <arpa/inet.h>  /* for sockaddr_in and inet_addr() */
#include <stdlib.h>     /* for exit() */
#include <string.h>     /* for memset() */
#include <unistd.h>     /* for close() */
#include <errno.h>      /* for errno*/

//macro
#define BUF_SIZE 1024 
#define PORT 1025 
#define SEND_PACK_CNT 3
#define SRV_IP "127.0.0.1" 
#undef handle_error
#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while(0)

int main(int argc, char *argv[])
{
    struct sockaddr_in server_addr;
    char buf[BUF_SIZE];
    int conn_fd = -1;
    int i;

    /*創建tcp描述符*/
    conn_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(conn_fd == -1)
        handle_error("socket");

    /*指定將連接的server信息*/
    memset(&server_addr, 0, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    if (inet_aton(SRV_IP, &server_addr.sin_addr) == 0)
        handle_error("inet_aton");

    /*連接到server*/
    if(connect(conn_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
        handle_error("connect");

    printf("connect to server ip:%s port:%d\n", SRV_IP, PORT);
    for(i = 0; i < SEND_PACK_CNT; i++) {
        sprintf(buf, "Packet %d", i);
        printf("send to server: %s\n", buf);
        /*發送數據*/
        send(conn_fd, buf, strlen(buf) + 1, 0);
        sleep(2);
    }

    /*關閉描述符*/
    printf("close\n");
    close(conn_fd);

    return 0;
}

TCP server和client的運行測試
這裏寫圖片描述

TCP C/S模型的思考
在TCP連接關閉時,連接會進入TIME_WAIT狀態,經過2個MSL時間後關閉,因此如果client頻繁連接和關閉,如果在2個MSL時間內令連接數達到1024則下一次將會返回失敗。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章