C 語言中開啟一個套接字
Jinku Hu
2023年10月12日
本文將介紹幾種如何在 C 語言中開啟套接字的方法。
使用 listen
和 accept
函式在 C 語言中開啟套接字
listen
和 accept
是 UNIX 網路功能的一部分,有時也稱為 Sockets API,它實現了處理程序間通訊的核心功能。請注意,套接字是通訊路徑端點的抽象表示。套接字的型別不同,例如 Unix 域和 Internet 域,但是它們都遵循相似的過程來建立兩個程序之間的通訊。
請記住,網路只是兩個正在執行的程式之間的通訊。首先,呼叫 socket
函式以建立端點和相應的控制代碼。套接字使用類似於 UNIX 系統上常規檔案或管道的檔案描述符進行處理。socket
呼叫之後,根據你所開發的程式型別,它有兩種情況。通常,我們在網路通訊中具有客戶端-伺服器模型,其中一個程式(伺服器)需要等待其他程式(客戶端)與其連線。
在此示例中,我們實現了一個伺服器端程式,該程式需要偵聽特定端點並接受客戶端連線。在這種情況下,在 socket
之後呼叫 bind
函式,以繫結到程式將接受傳入連線的特定地址。在 bind
之後,listen
和 accept
函式被呼叫後,形成所謂的被動套接字。注意,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