Linux應用開發之網絡套接字編程(實例篇)

服務端與客戶端單連接

服務端代碼

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \void * thread_write(void *argv)
{unsigned char * buf_sock_write;buf_sock_write = (unsigned char *)malloc(30);int client_fd = *((int *)argv);//以ctrl+D為結束信號,結束本端的寫線程while (read(STDIN_FILENO,buf_sock_write,30)){send(client_fd,buf_sock_write,30,0);memset(buf_sock_write,0,30);}printf("服務端收到關閉信號\n");//關閉WR時,對端的recv會收到一個0字節的數據包,從而結束對端讀線程,不同時關閉讀端是為了接收對端的關閉信號//本端關閉寫端,但是仍然可以接收數據包shutdown(client_fd,SHUT_WR);free(buf_sock_write);
}void * thread_read(void *argv)
{unsigned char * buf_sock_read;buf_sock_read = (unsigned char *)malloc(30);int client_fd = *((int *)argv);//對端關閉WR之后,本端的recv會接收到一個0字節的數據包,從而結束本端的讀線程while (recv(client_fd,buf_sock_read,30,0)){printf("%s",buf_sock_read);memset(buf_sock_read,0,30);}printf("客戶端斷開連接\n");free(buf_sock_read);
}int main(int argc, char const *argv[])
{int server_fd,tmp,client_fd;//創建服務端,客戶端ip地址結構體struct sockaddr_in server,client;memset(&server,0,sizeof(server));memset(&client,0,sizeof(client));//1,創建服務端套接字并返回文件描述符,家族選擇AF_INET(IPV4),使用TCP就選擇SOCK_STREAM,UDP就選擇SOCK_DGRAMserver_fd = socket(AF_INET,SOCK_STREAM,0);//2,設置服務端的ip和端口號server.sin_family = AF_INET;inet_pton(AF_INET,"0.0.0.0",&server.sin_addr);server.sin_port = htons(8888);//3,將套接字綁定地址,我們使用的是IPV4專屬的struct sockaddr_in結構體,需要強轉為專用的struct sockaddr *tmp = bind(server_fd,(struct sockaddr *)&server,sizeof(server));handle_error("bind",tmp);//4,服務端進入監聽模式,隨時準備接受連接tmp = listen(server_fd,128);handle_error("listen",tmp);//5,服務端接受客戶端的連接,同時會獲取到客戶端套接字的文件描述符和ip地址等信息,通過文件描述符和客戶端進行通信socklen_t client_addr_len;client_fd = accept(server_fd,(struct sockaddr *)&client,&client_addr_len);printf("客戶端連接成功\n");pthread_t thread_id_write;pthread_t thread_id_read;pthread_create(&thread_id_write,NULL,thread_write,(void *)&client_fd);pthread_create(&thread_id_read,NULL,thread_read,(void *)&client_fd);pthread_join(thread_id_write,NULL);pthread_join(thread_id_read,NULL);close(server_fd);close(client_fd);return 0;
}

客戶端代碼

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \void * thread_write(void *argv)
{int client_fd = *((int *)argv);unsigned char * buf_sock_write = (unsigned char *)malloc(30);//客戶端同樣以ctrl+D為關閉信號,關閉寫端while (read(STDIN_FILENO,buf_sock_write,30)){send(client_fd,buf_sock_write,30,0);memset(buf_sock_write,0,30);}printf("客戶端收到關閉信號\n");//關閉寫功能會向對端發送一個0字節的數據包,不同時關閉讀端是為了接收對端的關閉信號//本端關閉寫端,但是仍然可以接收數據包shutdown(client_fd,SHUT_WR);free(buf_sock_write);
}void * thread_read(void *argv)
{int client_fd = *((int *)argv);unsigned char * buf_sock_read = (unsigned char *)malloc(30);//對端關閉WR之后,本端的recv會接收到一個0字節的數據包,從而結束本端的讀線程while (recv(client_fd,buf_sock_read,30,0)){printf("%s",buf_sock_read);memset(buf_sock_read,0,30);}printf("服務端關閉連接\n");free(buf_sock_read);
}int main(int argc, char const *argv[])
{int client_fd,tmp;struct sockaddr_in server_addr,client_addr;memset(&server_addr,0,sizeof(server_addr));memset(&client_addr,0,sizeof(client_addr));//1,創建套接字client_fd = socket(AF_INET,SOCK_STREAM,0);handle_error("socket",client_fd);//2,設置服務端的ip地址和端口號inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8888);//端口號要和服務端設置的相匹配//3,綁定套接字,客戶端可以跳過本步驟,會自動分配ip地址和端口號inet_pton(AF_INET,"0.0.0.0",&client_addr.sin_addr);client_addr.sin_family = AF_INET;client_addr.sin_port = htons(6666);//要避免使用和客戶端相同的端口,因為是本機通信,端口不能被多個進程同時使用tmp = bind(client_fd,(struct sockaddr *)&client_addr,sizeof(client_addr));handle_error("bind",tmp);//4,連接服務端tmp = connect(client_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));handle_error("connect",tmp);printf("成功連接到服務端\n");pthread_t thread_id_write;pthread_t thread_id_read;pthread_create(&thread_id_write,NULL,thread_write,(void *)&client_fd);pthread_create(&thread_id_read,NULL,thread_read,(void *)&client_fd);pthread_join(thread_id_write,NULL);pthread_join(thread_id_read,NULL);close(client_fd);return 0;
}

過程及結果解釋

? ? ? ? 客戶端和服務端代碼均使用多線程,分別為讀寫線程,均通過終端接收數據并向對端發送數據,接收到對端的數據之后就會打印在終端上。在服務端/客戶端關閉本端的寫功能之后,都可以繼續接收對端的數據,但是無法發送數據。

????????在兩個終端中分別運行服務端和客戶端代碼,要先運行服務端代碼,不然客戶端會連接不到。

? ? ? ? ?服務端發送一次hello csdn,客戶端發送一次hello wrold

? ? ? ? ?關閉服務端,客戶端繼續發送數據

? ? ? ? 關閉客戶端?

?服務端基于多線程的支持多個客戶端連接

服務端代碼

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \void *thread_recv_send(void *argv)
{int client_fd; ssize_t cnt;client_fd = *((int *)argv);char *buf = (char *)malloc(100);while (cnt = recv(client_fd,buf,100,0)){handle_error("recv",cnt);cnt = send(client_fd,buf,cnt,0);handle_error("send",cnt);printf("客戶端%d發來消息,內容為%s",client_fd,buf);memset(buf,0,100);}shutdown(client_fd,SHUT_WR);printf("有客戶端斷開連接,文件描述符為%d\n",client_fd);free(buf);close(client_fd);
}int main(int argc, char const *argv[])
{int server_fd,client_fd,tmp;struct sockaddr_in server_addr,client_addr;//創建服務端套接字server_fd = socket(AF_INET,SOCK_STREAM,0);handle_error("socket",server_fd);//設置服務端ip地址和端口號server_addr.sin_family = AF_INET;inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);server_addr.sin_port = htons(8888);//綁定服務端套接字tmp = bind(server_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));handle_error("bind",tmp);tmp = listen(server_fd,128);handle_error("listen",tmp);while (1){socklen_t sock_len;client_fd = accept(server_fd,(struct sockaddr *)&client_addr,&sock_len);handle_error("accept",client_fd);printf("有客戶端連接,ip地址為%s,端口號為%d,文件描述符為%d\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port),client_fd);pthread_t thread_id_sock;pthread_create(&thread_id_sock,NULL,thread_recv_send,(void *)&client_fd);pthread_detach(thread_id_sock);}close(server_fd);return 0;
}

客戶端代碼

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \void * thread_write(void *argv)
{int client_fd = *((int *)argv);unsigned char * buf_sock_write = (unsigned char *)malloc(30);//客戶端同樣以ctrl+D為關閉信號,關閉寫端while (read(STDIN_FILENO,buf_sock_write,30)){send(client_fd,buf_sock_write,30,0);memset(buf_sock_write,0,30);}printf("客戶端收到關閉信號\n");//關閉寫功能會向對端發送一個0字節的數據包,不同時關閉讀端是為了接收對端的關閉信號//本端關閉寫端,但是仍然可以接收數據包shutdown(client_fd,SHUT_WR);free(buf_sock_write);
}void * thread_read(void *argv)
{int client_fd = *((int *)argv);unsigned char * buf_sock_read = (unsigned char *)malloc(30);//對端關閉WR之后,本端的recv會接收到一個0字節的數據包,從而結束本端的讀線程while (recv(client_fd,buf_sock_read,30,0)){printf("%s",buf_sock_read);memset(buf_sock_read,0,30);}printf("服務端關閉連接\n");free(buf_sock_read);
}int main(int argc, char const *argv[])
{int client_fd,tmp;struct sockaddr_in server_addr,client_addr;memset(&server_addr,0,sizeof(server_addr));memset(&client_addr,0,sizeof(client_addr));//1,創建套接字client_fd = socket(AF_INET,SOCK_STREAM,0);handle_error("socket",client_fd);//2,設置服務端的ip地址和端口號inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8888);//端口號要和服務端設置的相匹配//3,綁定套接字,客戶端可以跳過本步驟,會自動分配ip地址和端口號,如果固定端口號的話//多個線程無法共用一個端口,導致無法創建多連接/*inet_pton(AF_INET,"0.0.0.0",&client_addr.sin_addr);client_addr.sin_family = AF_INET;client_addr.sin_port = htons(6666);//要避免使用和客戶端相同的端口,因為是本機通信,端口不能被多個進程同時使用tmp = bind(client_fd,(struct sockaddr *)&client_addr,sizeof(client_addr));handle_error("bind",tmp);*///4,連接服務端tmp = connect(client_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));handle_error("connect",tmp);printf("成功連接到服務端\n");pthread_t thread_id_write;pthread_t thread_id_read;pthread_create(&thread_id_write,NULL,thread_write,(void *)&client_fd);pthread_create(&thread_id_read,NULL,thread_read,(void *)&client_fd);pthread_join(thread_id_write,NULL);pthread_join(thread_id_read,NULL);close(client_fd);return 0;
}

過程及結果解釋

? ? ? ? 客戶端的代碼幾乎沒有變化,只是取消了ip地址、端口號與套接字的綁定,因為客戶端會默認設置本機的ip地址,并選擇一個空閑端口使用,所以無需進行綁定。相反如果固定了端口號,因為端口號不能被多個進程共用,反而會導致無法創建多連接。

? ? ? ? 服務端的接受連接的邏輯與之前相同,在每次接受連接之后都會創建一個新線程并傳入服務端的套接字,并將線程設置為detach狀態,使其在結束之后自動回收資源,不使用join,從而不會阻擋創立新連接。在新線程中維護與客戶端的連接并進行數據傳輸,將服務端接收到的消息發送回客戶端并打印到終端上。當客戶端從終端中接收了ctrl+D之后,客戶端退出,調用shutdown會向服務端發送揮手信號,此時recv接收到的數據長度為0,服務端也隨之退出。

? ? ? ? 打開三個終端,在終端1中運行服務端,終端2,3運行客戶端

? ? ? ? ?終端2,3分別發送消息

? ? ? ? ?終端2,3退出

? ? ? ? 可以看到此時客戶端已經全部退出,服務端仍然在接收連接

? ? ? ? 終端2,3繼續運行客戶端,與服務端建立連接?

?服務端基于多進程的支持客戶端多連接

服務端代碼

#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \void singal_handle(int sig)
{while (waitpid(-1,NULL,WNOHANG)){}}void recv_send(int client_fd,struct sockaddr_in * client_addr)
{ssize_t cnt;char *buf = malloc(30);while (cnt = recv(client_fd,buf,30,0)){send(client_fd,buf,cnt,0);printf("客戶端%d發來消息,內容為%s",ntohs(client_addr->sin_port),buf);memset(buf,0,30);}printf("客戶端%d斷開連接\n",client_fd);shutdown(client_fd,SHUT_WR);free(buf);
}int main(int argc, char const *argv[])
{int server_fd,client_fd,tmp;struct sockaddr_in server_addr,client_addr;server_fd = socket(AF_INET,SOCK_STREAM,0);handle_error("socket",server_fd);server_addr.sin_family = AF_INET;inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);server_addr.sin_port = htons(8888);tmp = bind(server_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));handle_error("bind",tmp);tmp = listen(server_fd,128);handle_error("listen",tmp);//注冊信號處理函數,調用waitpid回收子進程防止成為僵尸進程signal(SIGCHLD,singal_handle);while (1){socklen_t client_len;client_fd = accept(server_fd,(struct sockaddr*)&client_addr,&client_len);handle_error("accept",client_fd);printf("有客戶端建立連接,ip地址為%s,端口號為%d,文件描述符為%d\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port),client_fd);pid_t pid = fork();if(pid == 0)//子進程{//子進程不需要使用服務端描述符close(server_fd);recv_send(client_fd,&client_addr);close(client_fd);exit(EXIT_SUCCESS);}else if(pid > 0){//父進程不需要使用客戶端描述符close(client_fd);}}return 0;
}

客戶端代碼

? ? ? ? 客戶端代碼和多線程的相同。

運行結果及過程解釋

? ? ? ? 服務端通過fork創建多個進程來維護與客戶端的連接,在子進程中執行數據傳輸,父進程阻塞等待客戶端的連接。子進程在客戶端斷開之后調用exit退出進程,向父進程發送SIGCHLD信號,父進程通過注冊信號處理函數調用waitpid來回收每一個子進程,防止出現僵尸進程。

? ? ? ? 程序的運行結果和多線程的基本相同。開啟三個終端,終端1執行服務端,2,3執行客戶端并發送消息,之后關閉客戶端

? ? ? ? ?和多線程不同的是,我們發現兩個客戶端的文件描述符是相同的,這是因為accept在父進程進行,數據傳輸的任務在子進程執行,父進程不需要使用客戶端的文件描述符,所以已經將其關閉了,進而導致下一次返回的文件描述符相同。

UDP的數據傳輸

????????UDP通訊也使用socket,但是接收和發送的函數與TCP不一樣。由于UDP不存在握手這一步驟,所以在綁定地址之后,服務端不需要listen,客戶端也不需要connect,服務端同樣不需要accept。只要服務端綁定以后,就可以相互發消息了,由于沒有握手過程,兩端都不能確定對方是否收到消息,這也是UDP協議不如TCP協議可靠的地方。

? ? ? ? UDP的通訊流程相比之下就簡單了很多,TCP是通過accept和connect綁定文件描述符進行通信,UDP則是通過目標地址的ip和端口號來通信,使用的是struct sockaddr結構體的信息。在接收對方的數據之后,就可以獲得對方的ip地址和端口號等信息,進而向對方回傳數據。

服務端代碼

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \int main(int argc, char const *argv[])
{//UDP通訊過程中只涉及一個套接字就是本端套接字int sock_fd,tmp;char buf[30];struct sockaddr_in server_addr,client_addr;socklen_t client_len = sizeof(client_addr);sock_fd = socket(AF_INET,SOCK_DGRAM,0);handle_error("sock",sock_fd);server_addr.sin_family = AF_INET;inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);server_addr.sin_port = htons(8888);tmp = bind(sock_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));handle_error("bind",tmp);ssize_t cnt = 0;while (cnt = recvfrom(sock_fd,buf,30,0,(struct sockaddr *)&client_addr,&client_len)){printf("%s",buf);sendto(sock_fd,buf,30,0,(struct sockaddr *)&client_addr,client_len);memset(buf,0,30);}printf("客戶端退出\n");return 0;
}

客戶端代碼

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \int main(int argc, char const *argv[])
{int sock_fd,tmp;sock_fd = socket(AF_INET,SOCK_DGRAM,0);handle_error("socket",sock_fd);struct sockaddr_in server_addr;socklen_t server_len;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8888);inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);char buf[30];while (read(STDIN_FILENO,buf,30)){sendto(sock_fd,buf,30,0,(struct sockaddr *)&server_addr,sizeof(server_addr));memset(buf,0,30);recvfrom(sock_fd,buf,30,0,(struct sockaddr *)&server_addr,&server_len);printf("%s",buf);memset(buf,0,30);}sendto(sock_fd,"",0,0,(struct sockaddr *)&server_addr,sizeof(server_addr));return 0;
}

? ? ? ? 客戶端必須先向服務端發送消息,之后服務端才可以獲取到客戶端的ip地址和端口號。運行結果如下

套接字用于進程間通信

????????Socket編程原本是為了網絡服務的,后來逐漸發展成一種進程間通信的方式:Unix Domain Socket IPC。它允許在同一臺主機上運行的進程之間進行高效的數據傳輸,無需經過網絡協議棧,因此具有低延遲和高性能的特點。通過文件系統中的特殊文件(通常是一個套接字文件),進程可以通過套接字(socket)來進行通信,實現雙向的數據傳輸。

? ? ? ? 和網絡通信不同的是,UNIX通信域使用的結構體是struct sockaddr_un,結構體的sun_family固定使用AF_UNIX,第二個成員變為了套接字的地址,也就是聲明一個使用本地文件的socket,其他進程可以綁定該文件完成進程間通信。在使用完成之后,服務端需要調用unlink清除該套接字文件,否則下次進行綁定時會提示該地址已經被使用。除此之外,服務端和客戶端的程序編寫流程和TCP并無區別。

? ? ? ? 服務端代碼

#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \void *thread_recv_send(void *argv)
{int client_fd; ssize_t cnt;client_fd = *((int *)argv);char *buf = (char *)malloc(100);while (cnt = recv(client_fd,buf,100,0)){handle_error("recv",cnt);cnt = send(client_fd,buf,cnt,0);handle_error("send",cnt);printf("客戶端%d發來消息,內容為%s",client_fd,buf);memset(buf,0,100);}shutdown(client_fd,SHUT_WR);printf("有客戶端斷開連接,文件描述符為%d\n",client_fd);free(buf);close(client_fd);
}int main(int argc, char const *argv[])
{int server_fd,client_fd,tmp;struct sockaddr_un server_addr,client_addr;//創建套接字server_fd = socket(AF_UNIX,SOCK_STREAM,0);handle_error("socket",server_fd);//設置地址server_addr.sun_family = AF_UNIX;strcpy(server_addr.sun_path,"test.socket");//綁定套接字tmp = bind(server_fd,(struct sockaddr*)&server_addr,sizeof(server_addr));handle_error("bind",tmp);tmp = listen(server_fd,128);handle_error("listen",tmp);while (1){socklen_t client_len = sizeof(client_addr);client_fd = accept(server_fd,(struct sockaddr*)&client_addr,&client_len);handle_error("accept",client_fd);printf("有客戶端連接,文件描述符為%d\n",client_fd);pthread_t thread_id_sock;pthread_create(&thread_id_sock,NULL,thread_recv_send,(void *)&client_fd);pthread_detach(thread_id_sock);}unlink("test.socket");return 0;
}

客戶端代碼

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \void * thread_write(void *argv)
{int client_fd = *((int *)argv);unsigned char * buf_sock_write = (unsigned char *)malloc(30);//客戶端同樣以ctrl+D為關閉信號,關閉寫端while (read(STDIN_FILENO,buf_sock_write,30)){send(client_fd,buf_sock_write,30,0);memset(buf_sock_write,0,30);}printf("客戶端收到關閉信號\n");//關閉寫功能會向對端發送一個0字節的數據包,不同時關閉讀端是為了接收對端的關閉信號//本端關閉寫端,但是仍然可以接收數據包shutdown(client_fd,SHUT_WR);free(buf_sock_write);
}void * thread_read(void *argv)
{int client_fd = *((int *)argv);unsigned char * buf_sock_read = (unsigned char *)malloc(30);//對端關閉WR之后,本端的recv會接收到一個0字節的數據包,從而結束本端的讀線程while (recv(client_fd,buf_sock_read,30,0)){printf("%s",buf_sock_read);memset(buf_sock_read,0,30);}printf("服務端關閉連接\n");free(buf_sock_read);
}int main(int argc, char const *argv[])
{int client_fd,tmp;struct sockaddr_un server_addr,client_addr;memset(&server_addr,0,sizeof(server_addr));memset(&client_addr,0,sizeof(client_addr));//創建套接字client_fd = socket(AF_UNIX,SOCK_STREAM,0);handle_error("socket",client_fd);//設置服務端的地址server_addr.sun_family = AF_UNIX;strcpy(server_addr.sun_path,"test.socket");//連接服務端tmp = connect(client_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));handle_error("connect",tmp);printf("成功連接到服務端\n");pthread_t thread_id_write;pthread_t thread_id_read;pthread_create(&thread_id_write,NULL,thread_write,(void *)&client_fd);pthread_create(&thread_id_read,NULL,thread_read,(void *)&client_fd);pthread_join(thread_id_write,NULL);pthread_join(thread_id_read,NULL);close(client_fd);return 0;
}

過程解釋及運行結果

? ? ? ? 服務端仍然采用多線程方式支持多個連接,接收客戶端數據之后打印在終端并傳回客戶端。

?????????總結一下,套接字用于進程間通信時,類似于TCP協議下的編程順序,但是套接字地址結構體變為了struct sockaddr_un 其中的family寫為AF_UNIX,第二個成員改為了路徑,客戶端服務端的路徑要一致。創建套接字的第二個參數還是sock_stream和TCP一樣。服務端的編程順序是初始化地址,給地址賦值,創建套接字,綁定套接字,進入監聽模式,接受連接(會返回客戶端的套接字和地址),發送/接收數據,unlink釋放套接字。客戶端的編程順序是初始化地址,給地址賦值,創建套接字,連接客戶端,發送/接收數據。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/908849.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/908849.shtml
英文地址,請注明出處:http://en.pswp.cn/news/908849.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python SQLModel 簡介

銷量過萬TEEIS德國護膝夏天用薄款 優惠券冠生園 百花蜂蜜428g 擠壓瓶純蜂蜜巨奇嚴選 鞋子除臭劑360ml 多芬身體磨砂膏280g健70%-75%酒精消毒棉片濕巾1418cm 80片/袋3袋大包清潔食品用消毒 優惠券AIMORNY52朵紅玫瑰永生香皂花同城配送非鮮花七夕情人節生日禮物送女友 熱賣妙潔棉…

【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL

ETABS API實戰:導出框架元素數據到Excel 在結構工程師的日常工作中,經常需要從ETABS模型中提取框架元素信息進行后續分析。手動復制粘貼不僅耗時,還容易出錯。今天我們來用簡單的VBA代碼實現自動化導出。 ?? 我們要實現什么? 一鍵點擊,就能將ETABS中所有框架元素的基…

springboot根據部署服務器生成機器碼+加密生成到期時間授權碼設置項目在服務器的到期時間

生成機器碼 首先需要在后端寫個獲取window或linux的機器碼&#xff0c;根據CPU序列號和硬盤序列號(Windows)&#xff0c;拼接得到 /*** 操作系統的工具類*/ public class OSUtils {/*** 獲取window or linux機器碼** return*/public static String getOSNumber() {Map<Str…

Thumb-2指令集及其與STM32的關系

Thumb-2指令集及其與STM32的關系&#xff1a; 1. Thumb-2指令集是什么&#xff1f; 本質&#xff1a;Thumb-2是ARM公司設計的混合指令集架構&#xff0c;首次在ARMv7架構中引入&#xff08;如Cortex-M3/M4/M7&#xff09;。 核心創新&#xff1a; 融合了傳統 32位ARM指令&…

Haption 力反饋遙操作機器人:6 自由度 + 低延遲響應,解鎖精準遠程操控體驗

Haption自2001年成立以來&#xff0c;始終專注于力反饋設備與定制化解決方案的設計、研發及銷售。作為工業級力反饋技術的先行者&#xff0c;其核心產品以高精度交互與可靠性著稱&#xff0c;已與達索系統、空客、Orano 等行業頭部企業達成深度合作&#xff0c;業務覆蓋工程仿真…

C# ExcelWorksheet 貼圖

C# ExcelWorksheet 貼圖 在C#中,如果你想在Excel工作表中插入圖片(例如,在ExcelWorksheet中貼圖),你可以使用ClosedXML或EPPlus這樣的庫來操作Excel文件。下面我將分別介紹如何使用這兩個庫來實現這一功能。 使用ClosedXML 首先,確保你已經安裝了ClosedXML包。你可以通…

Android15默認授權浮窗權限

我們經常有那種需求&#xff0c;客戶需要定制的apk集成在ROM中&#xff0c;并且默認授予其【顯示在其他應用的上層】權限&#xff0c;也就是我們常說的浮窗權限&#xff0c;那么我們就可以通過以下方法在wms、ams等系統服務的systemReady()方法中調用即可實現預置應用默認授權浮…

莫蘭迪高級灰總結計劃簡約商務通用PPT模版

莫蘭迪高級灰總結計劃簡約商務通用PPT模版&#xff0c;莫蘭迪調色板清新簡約工作匯報PPT模版&#xff0c;莫蘭迪時尚風極簡設計PPT模版&#xff0c;大學生畢業論文答辯PPT模版&#xff0c;莫蘭迪配色總結計劃簡約商務通用PPT模版&#xff0c;莫蘭迪商務匯報PPT模版&#xff0c;…

無人機EN 18031歐盟網絡安全認證詳細解讀

EN 18031 是歐盟針對無線電設備發布的網絡安全標準&#xff0c;于 2024 年 8 月正式發布&#xff0c;2025 年 1 月 30 日被列入《無線電設備指令》&#xff08;RED&#xff09;協調標準清單&#xff0c;并于 2025 年 8 月 1 日起強制執行。以下是對無人機 EN 18031 歐盟網絡安全…

Linux離線(zip方式)安裝docker

目錄 基礎信息操作系統信息docker信息 安裝實例安裝步驟示例 遇到的問題問題1&#xff1a;修改默認工作路徑啟動失敗問題2 找不到對應組 基礎信息 操作系統信息 OS版本&#xff1a;CentOS 7 64位 內核版本&#xff1a;3.10.0 相關命令&#xff1a; uname -rcat /etc/os-rele…

Go 語言中switch case條件分支語句

1. 基本語法 package main import "fmt" func main() {var extname ".css"switch extname {case ".html":fmt.Println("text/html")case ".css":fmt.Println("text/css") // text/csscase ".js":fmt.…

FFmpeg:Windows系統小白安裝及其使用

一、安裝 1.訪問官網 Download FFmpeg 2.點擊版本目錄 3.選擇版本點擊安裝 注意這里選擇的是【release buids】&#xff0c;注意左上角標題 例如我安裝在目錄 F:\FFmpeg 4.解壓 5.添加環境變量 把你解壓后的bin目錄&#xff08;即exe所在文件夾&#xff09;加入系統變量…

LLM基礎2_語言模型如何文本編碼

基于GitHub項目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 字節對編碼(BPE) 上一篇博文說到 為什么GPT模型不需要[PAD]和[UNK]&#xff1f; GPT使用更先進的字節對編碼(BPE)&#xff0c;總能將詞語拆分成已知子詞 為什么需要BPE&#xff1f; 簡…

監控升級:可視化如何讓每一個細節 “說話”

你有沒有遇到過這樣的情況&#xff1f; 監控畫面里明明有“異常”&#xff0c;但值班人員愣是沒發現&#xff1b; 報警響起卻不知道具體發生了什么&#xff0c;只能靠猜、靠翻錄像&#xff1b; 出了事回看錄像&#xff0c;才發現線索早就在眼前&#xff0c;只是沒人注意到………

單片機bootloader(APP的自我復制)

文章目錄 Bootloader 中 APP 的自我復制與啟動機制解析一、為什么要進行自我復制?二、程序整體結構概述三、匯編啟動代碼分析重點解釋:四、C 語言部分分析核心功能:五、start\_app 函數:手動啟動指定 APP六、總結七、適用場景Bootloader 中 APP 的自我復制與啟動機制解析 …

瀏覽器工作原理11 [#] this:從JavaScript執行上下文視角講this

引用 《瀏覽器工作原理與實踐》 在上篇文章中&#xff0c;我們講了詞法作用域、作用域鏈以及閉包&#xff0c;并在最后思考題中留了下面這樣一段代碼 var bar {myName:"time.geekbang.com",printName: function () {console.log(myName)} } function foo() {le…

【C語言】-遞歸

1、遞歸概念 遞歸&#xff08;Recursion&#xff09;是編程中一種重要的解決問題的方法&#xff0c;其核心思想是函數通過調用自身來解決規模更小的子問題&#xff0c;直到達到最小的、可以直接解決的基準情形&#xff08;Base Case&#xff09;。 核心&#xff1a;自己調用…

12.5Swing控件3Jpanel JOptionPane

JPanel JPanel是一個輕量級容器組件&#xff0c;用于組織和管理其他 GUI 組件。它繼承自JComponent類&#xff0c;屬于javax.swing包&#xff0c;可以容納按鈕、文本框、標簽等控件 JPanel 默認使用的布局管理器是 FlowLayout&#xff0c;也可以嵌套其他面板&#xff0c;以便…

MIPI信號為什么不能進行長距離傳輸

1.關于MIPI信號傳輸 MIPI信號是不適合長距離傳輸的。 2.MIPI的信號擺幅小&#xff0c;抗干擾能力比較弱 MIPI信號的差分擺幅比較小&#xff0c;通常只有100mV~200mV,遠遠低于LVDS的350mV的擺幅 小擺幅信號在長線纜上傳輸的時候更容易被噪聲淹沒&#xff0c;信噪比下降&#xf…

Qt的學習(二)

1. 創建Hello Word 兩種方式&#xff0c;實現helloworld&#xff1a; 1.通過圖形化的方式&#xff0c;在界面上創建出一個控件&#xff0c;顯示helloworld 2.通過純代碼的方式&#xff0c;通過編寫代碼&#xff0c;在界面上創建控件&#xff0c; 顯示hello world&#xff1b; …