一、套接字(Socket)
套接字是進行網絡編程時非常重要的概念,它是一種通信的機制,可以在不同主機之間進行通信。套接字在網絡編程中扮演着重要的角色,下面我們來一步步地了解它。
1.創建套接字
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
socket()函數可以創建一個新的套接字。其中,domain參數指定了通信協議的領域,如AF_INET表示IPv4協議族,AF_INET6表示IPv6協議族。type參數指定了套接字的類型,如SOCK_STREAM表示TCP類型。protocol參數指定使用的協議,一般為0。
2.綁定套接字
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr \*addr, socklen_t addrlen);
bind()函數將本地的Socket地址和創建的Socket進行綁定。其中,sockfd參數即為socket()函數返回的文件描述符。addr參數是一個指向本地Socket地址結構體的指針。addrlen參數是該結構體的長度。
3.監聽套接字
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
listen()函數將sockfd指定的Socket轉換成被動式的Socket,backlog指定了連接請求隊列的最大長度。
4.接收連接請求
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr \*addr, socklen_t \*addrlen);
accept()函數從連接請求隊列中取得一個連接請求,生成一個全新的Socket進行通信。其中,sockfd為listen()函數創建的Socket。addr和addrlen參數表示請求連接的客戶端的地址信息。
二、進程和線程
進程和線程是操作系統中非常基礎的概念,本篇文章着重介紹在網絡編程中如何使用它們。
1.進程
進程是操作系統中進行資源分配和調度的基本單位。在網絡編程中,我們可以使用fork()函數創建一個新進程,讓進程分別運行不同的代碼。
#include <unistd.h>
pid_t fork(void);
fork()函數會創建一個新的進程,並將父進程的數據複製到子進程中。父子進程是並發的,但是它們運行的是同一個代碼。我們可以通過進程間的通信(IPC),使它們在運行過程中進行信息的交互。
2.線程
線程是進程中的更小單位,它可以輕鬆的與其他線程共享進程數據,便於實現並發。在Linux系統中,線程的實現依賴於POSIX的線程庫。
#include <pthread.h>
int pthread_create(pthread_t \*thread, const pthread_attr_t \*attr, void *(*start_routine) (void *), void \*arg);
pthread_create()函數可以創建一個新的線程,其中參數thread是線程的標識符,attr指向線程屬性的結構,start_routine是線程的入口地址,arg參數將傳遞給入口函數。
三、並發編程
在網絡編程中,進程和線程是實現並發編程的基本手段。當多個客戶端同時連接服務器時,我們需要一種方法來同時處理這些連接請求,這時候並發編程便派上了用場。
1.多進程實現並發
#include <sys/wait.h>
pid_t pid;
switch (pid = fork()) {
case -1:
perror("fork");
break;
case 0:
/* child */
exit(0);
break;
default:
/* parent */
waitpid(pid, NULL, 0);
break;
上述代碼中,我們可以在主進程中調用fork()函數創建一個子進程來處理一個請求。這樣,就可以同時處理多個請求。通過使用進程的基於文件描述符的機制可以實現進程之間的通信。
2.多線程實現並發
#include <pthread.h>
int threads_num;
pthread_t threads[THREAD_NUM];
/* create threads */
for (int i = 0; i < threads_num; i++) {
if (pthread_create(&threads[i], NULL, worker, NULL)) {
perror("pthread_create failed");
exit(1);
}
}
/* join threads */
for (int i = 0; i < threads_num; i++) {
if (pthread_join(threads[i], NULL)) {
perror("pthread_join failed");
exit(1);
}
}
上述代碼中,我們使用pthread_create()函數創建若干個線程,並通過pthread_join()函數等待線程結束。線程可以共享數據,且線程和線程之間的切換速度比進程之間的切換速度要快得多。
四、網絡編程實踐
下面我們通過一個簡單的例子來演示Linux網絡編程的過程。
1.創建Socket
#include <sys/types.h>
#include <sys/socket.h>
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket error");
exit(1);
}
上述代碼中,我們使用socket()函數創建一個新的Socket。其中,參數AF_INET表示使用IPv4協議族,SOCK_STREAM表示使用TCP協議。
2.綁定Socket
#include <sys/types.h>
#include <sys/socket.h>
struct sockaddr_in server_addr;
bzero(\&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(port);
int ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (ret == -1) {
perror("bind error");
exit(1);
}
上述代碼中,我們使用bind()函數將Socket與本地的IP地址和端口進行綁定。
3.監聽Socket
#include <sys/types.h>
#include <sys/socket.h>
int ret = listen(sockfd, backlog);
if (ret == -1) {
perror("listen error");
exit(1);
}
上述代碼中,我們使用listen()函數將Socket轉化為可接受的請求,backlog參數指定了連接請求隊列的長度。
4.接受連接請求
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
int connfd = accept(sockfd, (struct sockaddr *)&client_addr, &len);
if (connfd == -1) {
perror("accept error");
exit(1);
}
printf("accept from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
上述代碼中,我們使用accept()函數從連接請求隊列中取出一個連接請求生成一個全新的Socket,客戶端的地址信息保存在client_addr結構體中,我們可以通過inet_ntoa()和ntohs()函數將網絡位元組順序的IP地址和端口轉換為可讀形式。
5.接收和發送消息
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
上述代碼中,我們使用recv()和send()函數進行接收和發送消息,分別對應於TCP中的read()和write()函數。
結論
以上便是Linux網絡編程的詳解,我們介紹了套接字、進程和線程、並發編程、以及通過一個簡單例子演示了網絡編程的基本流程。在進一步的使用和探索中,相信本篇文章為讀者提供了很好的指引。
原創文章,作者:LWXLV,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/370615.html