Linux網絡編程是在Linux操作系統環境下進行的網絡相關程序開發,主要用于實現不同計算機之間的數據通信和資源共享。以下從基礎知識、網絡編程模型、常用函數和編程步驟等方面進行詳細介紹:
基礎知識
1. 網絡協議
-
TCP/IP協議族:是互聯網通信的基礎協議,包括多個層次的協議。例如,IP協議負責網絡層的數據包傳輸,TCP和UDP協議則工作在傳輸層。
-
TCP(傳輸控制協議):提供面向連接、可靠的、基于字節流的傳輸服務。在進行數據傳輸前需要建立連接,傳輸完成后需要斷開連接,適合對數據準確性要求較高的場景,如文件傳輸、網頁瀏覽等。
-
UDP(用戶數據報協議):提供無連接、不可靠的傳輸服務。不需要建立連接,數據以數據報的形式發送,適合對實時性要求較高、對數據準確性要求相對較低的場景,如視頻會議、實時游戲等。
-
2. 網絡地址
-
IP地址:用于唯一標識網絡中的設備,分為IPv4和IPv6兩種。IPv4地址由32位二進制數組成,通常表示為點分十進制形式,如
192.168.1.1
;IPv6地址由128位二進制數組成,采用冒號分隔的十六進制表示。 -
端口號:用于區分同一設備上不同的網絡應用程序,范圍從0到65535。其中,0 - 1023為系統保留端口,通常由系統服務使用,如HTTP服務默認使用80端口,HTTPS服務默認使用443端口。
網絡編程模型
1. 客戶端 - 服務器模型
-
這是最常見的網絡編程模型,由客戶端和服務器兩部分組成。服務器監聽特定的端口,等待客戶端的連接請求;客戶端主動發起連接請求,與服務器建立連接后進行數據交互。
2. 多進程/多線程模型
-
多進程模型:服務器為每個客戶端連接創建一個新的進程來處理,各個進程之間相互獨立,一個進程的崩潰不會影響其他進程。但創建和銷毀進程的開銷較大,會消耗較多的系統資源。
-
多線程模型:服務器為每個客戶端連接創建一個新的線程來處理,線程共享進程的資源,創建和銷毀線程的開銷相對較小。但線程之間的同步和互斥問題需要處理,否則可能會出現數據不一致的情況。
3. I/O多路復用模型
-
使用
select
、poll
或epoll
等函數,讓一個進程可以同時監聽多個文件描述符(包括網絡套接字)的讀寫事件,當某個文件描述符有事件發生時,通知進程進行相應的處理。這種模型可以提高服務器的并發處理能力,減少系統資源的消耗。
常用函數
1. 套接字相關函數
-
socket():用于創建一個套接字,返回一個文件描述符。例如:
#include <sys/socket.h>
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
其中,AF_INET
表示使用IPv4地址族,SOCK_STREAM
表示使用TCP協議。
-
bind():將套接字與指定的IP地址和端口號綁定。例如:
#include <arpa/inet.h>
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(8888);
bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
-
listen():將套接字設置為監聽狀態,等待客戶端的連接請求。例如:
listen(sockfd, 5);
其中,5
表示允許的最大連接請求隊列長度。
-
accept():接受客戶端的連接請求,返回一個新的套接字描述符用于與客戶端進行通信。例如:
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int connfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_addr_len);
-
connect():客戶端使用該函數向服務器發起連接請求。例如:
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(8888);
connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
2. 數據讀寫函數
-
send()/recv():用于TCP套接字的數據發送和接收。例如:
// 發送數據
char *msg = "Hello, server!";
send(sockfd, msg, strlen(msg), 0);// 接收數據
char buffer[1024];
int n = recv(sockfd, buffer, sizeof(buffer), 0);
-
sendto()/recvfrom():用于UDP套接字的數據發送和接收,需要指定目標地址。例如:
// 發送數據
struct sockaddr_in server_addr;
// 初始化server_addr
sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));// 接收數據
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&client_addr, &client_addr_len);
編程步驟
1. 服務器端編程步驟
-
創建套接字(
socket()
)。 -
綁定套接字到指定的IP地址和端口號(
bind()
)。 -
將套接字設置為監聽狀態(
listen()
)。 -
接受客戶端的連接請求(
accept()
)。 -
與客戶端進行數據交互(
send()
/recv()
)。 -
關閉套接字(
close()
)。
2. 客戶端編程步驟
-
創建套接字(
socket()
)。 -
向服務器發起連接請求(
connect()
)。 -
與服務器進行數據交互(
send()
/recv()
)。 -
關閉套接字(
close()
)。
示例代碼
以下是一個簡單的TCP服務器和客戶端示例代碼:
服務器端代碼(server.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>#define PORT 8888int main() {int sockfd, connfd;struct sockaddr_in server_addr, client_addr;socklen_t client_addr_len = sizeof(client_addr);char buffer[1024];// 創建套接字sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd == -1) {perror("socket creation failed");exit(EXIT_FAILURE);}// 初始化服務器地址結構memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = INADDR_ANY;server_addr.sin_port = htons(PORT);// 綁定套接字if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("bind failed");exit(EXIT_FAILURE);}// 監聽套接字if (listen(sockfd, 5) == -1) {perror("listen failed");exit(EXIT_FAILURE);}printf("Server listening on port %d...\n", PORT);// 接受客戶端連接connfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_addr_len);if (connfd == -1) {perror("accept failed");exit(EXIT_FAILURE);}printf("Client connected.\n");// 接收客戶端數據int n = recv(connfd, buffer, sizeof(buffer), 0);if (n == -1) {perror("recv failed");exit(EXIT_FAILURE);}buffer[n] = '\0';printf("Received from client: %s\n", buffer);// 發送響應數據char *msg = "Hello, client!";send(connfd, msg, strlen(msg), 0);// 關閉套接字close(connfd);close(sockfd);return 0;
}
客戶端代碼(client.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>#define SERVER_IP "127.0.0.1"
#define PORT 8888int main() {int sockfd;struct sockaddr_in server_addr;char buffer[1024];// 創建套接字sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd == -1) {perror("socket creation failed");exit(EXIT_FAILURE);}// 初始化服務器地址結構memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);server_addr.sin_port = htons(PORT);// 連接服務器if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("connect failed");exit(EXIT_FAILURE);}printf("Connected to server.\n");// 發送數據char *msg = "Hello, server!";send(sockfd, msg, strlen(msg), 0);// 接收服務器響應int n = recv(sockfd, buffer, sizeof(buffer), 0);if (n == -1) {perror("recv failed");exit(EXIT_FAILURE);}buffer[n] = '\0';printf("Received from server: %s\n", buffer);// 關閉套接字close(sockfd);return 0;
}