《Linux網路開發必學教程》2_服務端程式設計初體驗

TianSong發表於2022-05-02

客戶端/服務端 程式設計模式

  • 服務端長期保留於網路(公開自己的 IP 地址),並等待客戶端連線
  • 客戶端發起連線動作,並等待服務端回應
  • 特點:

    • 服務端無法主動連線客戶端
    • 客戶端只能按照預定義的方式(協議)連線服務端

服務端程式設計模式

1. 準備網路連線
2. 繫結埠
3. 進入埠監聽狀態
4. 等待連線

image.png

服務端核心工作:繫結 & 監聽 & 接收

  • 繫結:int bind(int sock, struct sockaddr *addr, socklen_t addrlen);
  • 監聽:int listen(int sock, int backlog);
  • 接收:int accept(int sock, struct sockaddr *addr, socklen_t addrlen);

深度剖析服務端

  • 服務端 socket 只用於接收連線,不進行實際通訊
  • 當接收到連線時,accept() 函式返回與客戶端通訊的 socket
  • 服務端 socket 產生用於通訊的客戶端 socket
所以,socket 究竟是什麼玩意?如何理解?

深入理解 socket() 函式

  • socket() 是什麼?

    • socket() 是一個多功能函式
  • socket() 返回的又是什麼?

    • socket() 的返回值是用於通訊的資源識別符號
  • socket() 還能做什麼?

    • socket() 可提供不同型別的通訊功能(本地程式間通訊)

程式設計實驗:服務端程式設計初體驗

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

int main()
{
    int server = 0;
    struct sockaddr_in saddr = {0};
    int clinet = 0;
    struct sockaddr_in caddr = {0};
    socklen_t asize = 0;
    int len = 0;
    char buf[32] = {0};
    int r = 0;

    server = socket(PF_INET, SOCK_STREAM, 0);

    if (server == -1) {
        printf("server socket error\n");
        return -1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(8899);

    if (bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
        printf("server bind error\n");
        return -1;
    }

    if (listen(server, 1) == -1) {
        printf("server listen error\n");
        return -1;
    }

    printf("server start success\n");

    asize = sizeof(caddr);

    clinet = accept(server, (struct sockaddr*)&caddr, &asize);

    if (clinet == -1) {
        printf("client accept error\n");
        return -1;
    }

    printf("client: %d\n", clinet);

    len = 0;

    do {
        int i = 0;
        
        r = recv(clinet, buf, sizeof(buf), 0);

        if (r > 0) {
            len += r;
        }

        for (i=0; i<r; ++i) {
            printf("%c", buf[i]);
        }
    } while (len < 64);

    printf("\n");

    send(clinet, "hello word!", 12, 0);

    sleep(1);

    close(clinet);
    close(server);

    return 0;
}
INADDR_ANY => "0.0.0.0", 表示本機的連線全部接受(如,本機有多個網路卡進而有多個 IP 時)

客戶端/服務端 程式設計的核心模式

  • 服務端長時間執行(死迴圈)接收客戶端的請求
  • 客戶端連線後向服務端傳送請求(協議資料)

image.png

服務端核心程式設計模式

image.png

程式設計實驗:客戶端/服務端程式設計實驗

  1. 服務端持續監聽客戶端連線
  2. 服務端被連線後 echo 客戶端資料
  3. 服務端接收到 quit 後斷開連線
  4. 客戶端接收使用者輸入併傳送到服務端
服務端
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int server = 0;
    struct sockaddr_in saddr = {0};
    int client = 0;
    struct sockaddr_in caddr = {0};
    socklen_t asize = 0;
    int len = 0;
    char buf[32] = {0};
    int r = 0;

    server = socket(PF_INET, SOCK_STREAM, 0);

    if (server == -1) {
        printf("server socket error\n");
        return -1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(8888);

    if (bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
        printf("server bind error\n");
        return -1;
    }

    if (listen(server, 1) == -1) {
        printf("server listen error\n");
        return -1;
    }

    printf("server start success\n");

    while (1) {
        asize = sizeof(caddr);

        client = accept(server, (struct sockaddr*)&caddr, &asize);

        if (client == -1) {
            printf("client accept error\n");
            return -1;
        }

        printf("client: %d\n", client);

        do {
            r = recv(client, buf, sizeof(buf), 0);
            if (r > 0) {
                printf("Receive: %s\n", buf);

                if (strcmp(buf, "quit") != 0) {
                    len = send(client, buf, r, 0);
                }
                else {
                    break;
                }
            } 
        } while (r > 0);

        close(client);
    }

    close(server);

    return 0;
}
客戶端
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int sock = 0;
    struct sockaddr_in addr = {0};
    int len = 0;
    char buf[128] = {0};
    char input[32] = {0};
    int r = 0;

    sock = socket(PF_INET, SOCK_STREAM, 0);

    if (sock == -1) {
        printf("socket error\n");
        return -1;
    }

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("192.168.2.46");
    addr.sin_port = htons(8888);

    if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
        printf("connect error\n");
        return -1;
    }

    printf("connect success\n");

    while (1) {
        printf("Input: ");

        scanf("%s", input);

        len = send(sock, input, strlen(input) + 1, 0);

        r = recv(sock, buf, sizeof(buf), 0);

        if (r > 0) {
            printf("Receive: %s\n", buf);
        }
        else {
            break;
        }
    }

    close(sock);

    return 0;
}
思考:如何增強服務端能力,同時支援多個客戶端?

相關文章