C 語言中開啟一個套接字

Jinku Hu 2023年10月12日
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 <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.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 <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.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);
}
作者: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

DelftStack.com 創辦人。Jinku 在機器人和汽車行業工作了8多年。他在自動測試、遠端測試及從耐久性測試中創建報告時磨練了自己的程式設計技能。他擁有電氣/ 電子工程背景,但他也擴展了自己的興趣到嵌入式電子、嵌入式程式設計以及前端和後端程式設計。

LinkedIn Facebook

相關文章 - C Networking