C 語言中開啟一個套接字

Jinku Hu 2021年3月21日
C 語言中開啟一個套接字

本文將介紹幾種如何在 C 語言中開啟套接字的方法。

使用 listenaccept 函式在 C 語言中開啟套接字

listenaccept 是 UNIX 網路功能的一部分,有時也稱為 Sockets API,它實現了處理程序間通訊的核心功能。請注意,套接字是通訊路徑端點的抽象表示。套接字的型別不同,例如 Unix 域和 Internet 域,但是它們都遵循相似的過程來建立兩個程序之間的通訊。

請記住,網路只是兩個正在執行的程式之間的通訊。首先,呼叫 socket 函式以建立端點和相應的控制代碼。套接字使用類似於 UNIX 系統上常規檔案或管道的檔案描述符進行處理。socket 呼叫之後,根據你所開發的程式型別,它有兩種情況。通常,我們在網路通訊中具有客戶端-伺服器模型,其中一個程式(伺服器)需要等待其他程式(客戶端)與其連線。

在此示例中,我們實現了一個伺服器端程式,該程式需要偵聽特定端點並接受客戶端連線。在這種情況下,在 socket 之後呼叫 bind 函式,以繫結到程式將接受傳入連線的特定地址。在 bind 之後,listenaccept 函式被呼叫後,形成所謂的被動套接字。注意,accept 會阻塞該程序,直到不同的程序無法使用 connect 函式呼叫建立連線為止。

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

#define handle_error(msg) \
           do { perror(msg); exit(EXIT_FAILURE); } while (0)

#define SOCKET_PATH "/tmp/my.sock"

enum {LISTEN_QUEUE = 100};

int main(int argc, char *argv[]) {
    int listenfd, connfd;
    socklen_t len;
    struct sockaddr_un servaddr, cliaddr;

    listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (listenfd == -1)
        handle_error("socket");

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sun_family = AF_UNIX;
    strncpy(servaddr.sun_path, SOCKET_PATH,sizeof(servaddr.sun_path) - 1);

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

    if (listen(listenfd, LISTEN_QUEUE) == -1)
        handle_error("listen");

    len = sizeof(cliaddr);
    connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &len);
    if (connfd == -1)
        handle_error("accept");

    unlink(SOCKET_PATH);
    exit(EXIT_SUCCESS);
}

通常,accept 呼叫後面是實現傳入連線處理的程式碼。想要與該伺服器建立連線的外部程式需要知道地址和埠。假設它們對程序都是已知的,它會在 socket 呼叫之後呼叫 connect 函式,並有望與伺服器程序建立連線。建立連線後,兩個程序都可以從套接字描述符中讀和寫,因為它們具有雙向通道。在實際情況下,應該將多個客戶端程序同時連線到伺服器。連線處理程式碼應同時實現。否則,伺服器一次將不能為一個以上的客戶端提供服務。

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

#define handle_error(msg) \
           do { perror(msg); exit(EXIT_FAILURE); } while (0)

#define SOCKET_PATH "/tmp/my.sock"

enum {MAXLINE = 4096, LISTEN_QUEUE = 100};

int main(int argc, char *argv[]) {
    int listenfd, connfd;
    socklen_t len;
    struct sockaddr_un servaddr, cliaddr;
    char buf[MAXLINE];

    listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (listenfd == -1)
        handle_error("socket");

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sun_family = AF_UNIX;
    strncpy(servaddr.sun_path, SOCKET_PATH,sizeof(servaddr.sun_path) - 1);

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

    if (listen(listenfd, LISTEN_QUEUE) == -1)
        handle_error("listen");

    len = sizeof(cliaddr);
    connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &len);
    if (connfd == -1)
        handle_error("accept");

    size_t ret = read(connfd, buf, MAXLINE);
    if (ret == -1)
        handle_error("read");

    printf("read %d bytes\nmessage: %s\n", connfd, buf);

    unlink(SOCKET_PATH);
    exit(EXIT_SUCCESS);
}
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn

相關文章 - C Networking