socket編程入門實踐——基於TCP的echo服務器/客戶端

最近在學習Linux網絡編程,參考《TCP/IP網絡編程》寫下這個socket編程示例——基於TCP的echo服務器/客戶端。echo服務器,顧名思義就是將收到的客戶端數據原封不動地傳回客戶端。
無論多麼複雜的TCP服務器,其函數調用順序幾乎都是socket -> bind -> listen -> accept -> read/write -> close,因此掌握這些基本函數的使用方法是很重要的。以下是基於TCP的echo服務器的完整代碼(附帶詳細註釋):

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

#define BUF_SIZE 1024

// to handle errors
void errorHandling(char* message) {
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

int main(int argc, char *argv[]) {
    // sockets of the server and the client
    int serv_sock, clnt_sock;
    // buffer zone
    char message[BUF_SIZE];
    // the length of the message received
    int str_len;

    // the ip addresses of the server and the client
    struct sockaddr_in serv_adr, clnt_adr;
    // the size of the client address
    socklen_t clnt_adr_sz;

    // check if the number of arguments is correct
    if (argc != 3) {
        printf("Usage: %s <IP Address> <Port>\n", argv[0]);
        exit(1);
    }

    // get a server socket
    serv_sock = socket(PF_INET, SOCK_STREAM, 0);    // domain, service types (should be SOCK_STREAM because of TCP), detailed protocol(usually 0)
    // if failed
    if (serv_sock == -1) {
        errorHandling("socket() error");
    }

    // initialize the server socket address
    memset(&serv_adr, 0, sizeof(serv_adr));
    // set the address family
    serv_adr.sin_family = AF_INET;
    // set the port, convert host to network byte order
    serv_adr.sin_port = htons(atoi(argv[2]));
    // set the IPv4 address
    serv_adr.sin_addr.s_addr = inet_addr(argv[1]);

    // bind the server socket with server address
    if (bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1) {
        errorHandling("bind() error");
    }

    // listen for connections on the socket
    if (listen(serv_sock, 5) == -1) {    // 5 means that the maximum length to which the queue of pending connections for sockfd
        errorHandling("listen() error");
    }

    // get the size of the client address
    clnt_adr_sz = sizeof(clnt_adr);

    printf("======Waiting for client's request======\n");

    while (1) {
        // accept a connection on the socket
        clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
        if (clnt_sock == -1) {
            errorHandling("accept() error");
        } else {
            printf("Connected... \n");
        }

        // echo
        while ((str_len = read(clnt_sock, message, BUF_SIZE)) != 0) {
            write(clnt_sock, message, str_len);
            message[str_len] = '\0';
            printf("Message from client : %s\n",  message);
        }

        // close the socket of the client
        close(clnt_sock);
    }

    // close the socket of the server
    close(serv_sock);

    return 0;
}

TCP客戶端的函數調用順序幾乎都是socket -> connect -> read/write -> close,以下是基於TCP的echo客戶端的完整代碼(附帶詳細註釋):

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

#define BUF_SIZE 1024

// to handle errors
void errorHandling(char* message) {
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

int main(int argc, char* argv[]) {
    // the socket of the client
    int sock;
    // the buffer zone
    char message[BUF_SIZE];
    // the length of the message received
    int str_len, recv_len, recv_cnt;
    // the sock address of the server
    struct sockaddr_in serv_adr;

    // check if the number of arguments is correct
    if (argc != 3) {
        printf("Usage: %s <IP> <port> \n", argv[0]);
        exit(1);
    }

    // get a server socket
    sock = socket(PF_INET, SOCK_STREAM, 0);
    // if failed
    if (sock == -1) {
        errorHandling("socket() error");
    }

    // initialize the server socket address
    memset(&serv_adr, 0, sizeof(serv_adr));
    // set the address family
    serv_adr.sin_family = AF_INET;
    // set the IPv4 address
    serv_adr.sin_addr.s_addr = inet_addr(argv[1]);
    // set the port, convert host to network byte order
    serv_adr.sin_port = htons(atoi(argv[2]));

    // connect to the server socket
    if (connect(sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1) {  // if failed
        errorHandling("connect() error");
    } else {
        puts("Connected......");
    }

    while (1) {
        fputs("Input message(Q to quit): ", stdout);
        fgets(message, BUF_SIZE, stdin);
        // check if it is time to quit
        if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
            break;

        // send message to server
        str_len = write(sock, message, strlen(message));

        recv_len = 0;
        while (recv_len < str_len) {
            // read the message received
            recv_cnt = read(sock, &message[recv_len], BUF_SIZE - 1);
            if (recv_cnt == -1) {
                errorHandling("read() error");
            }
            recv_len += recv_cnt;
        }
        message[recv_len] = 0;
        printf("Message from server: %s", message);

    }

    close(sock);
    return 0;
}

客戶端運行結果如下:
在這裏插入圖片描述
服務端運行結果如下:
在這裏插入圖片描述

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