【进程通信】Socket
網絡通信 Socket
??實現網絡應用時要先從網絡提供的接口開始,幾乎所有計算機系統都將網絡協議的軟件實現作為操作系統的一部分,因此網絡應用程序編程接口(API)一般都是操作系統提供的。套接字接口Socket是大部分操作系統都支持的一種流行接口,為計算機本地應用提供接入網絡的功能。
??Socket(套接字)可以看成是兩個網絡應用程序進行通信時,各自通信連接中的端點,這是一個邏輯上的概念。它是網絡環境中進程間通信的API,使用中的每一個套接字都有其類型和一個與之相連進程。通信時其中一個網絡應用程序將要傳輸的一段信息寫入它所在主機的 Socket中,該 Socket通過與網卡(NIC)相連的傳輸介質將這段信息送到另外一臺主機的 Socket中,使對方能夠接收到這段信息。 Socket是由IP地址和端口結合的,提供向應用層進程傳送數據包的機制。socket把復雜的TCP/IP協議封裝了起來,只要用好socket相關的函數即可完成網絡通信。
?
?
Socket 類型
??socket主要提供了流(stream)和數據報(datagram)兩種通信機制,即流socket和數據報socket。
??流socket基于TCP協議,是一個有序、可靠、雙向字節流的通道,傳輸數據不會丟失、不會重復、順序也不會錯亂。
??數據報socket基于UDP協議,不需要建立和維持連接,可能會丟失或錯亂。UDP不是一個可靠的協議,對數據的長度有限制,但是它的速度比較高。
?
?
流Socket 工作流程
??Socket在工作中分為客戶端(Client)和服務端(Server)兩部分:
?
服務端工作流程:
創建socket
int socket(int domain, int type, int protocol) //domain表示將要使用的協議族,例如:AF_INET表示因特網協議族 //type表示通信的語義,例如:SOCK_STREAM表示字節流、SOCK_DGRAM表示面向消息 //protocol表示將要用到的特定協議,下面統一用0 //返回值是該套接字的句柄(handle),即引用該套接字時使用的標識符把IP地址和端口綁定到socket上
int bind(int socket, struct sockaddr *address, int addr_len) //socket是被綁定套接字的標識符 //address是包含本地服務器IP地址和TCP端口號的結構體 //addr_len是address的size //綁定成功返回0設置socket為監聽模式
int listen(int socket, backlog) //socket是被綁定套接字的標識符 //backlog是該套接字允許連接的數量 //設置成功返回0接受客戶端的連接請求
int accept(int socket, struct sockaddr *address, int *addr_len) //socket是被綁定套接字的標識符 //address是包含服務器IP地址和TCP端口號的結構體,建立連接后會被寫入客戶端的對應信息 //addr_len是address的size //此函數是個阻塞操作,當建立連接時才返回,返回值是遠程客戶端的socket標識符與客戶端重復通信,直到客戶端斷開連接
int send(int socket, char *message, int msg_len, int flags) //把message信息發送給對應socket的遠程端,message長度為msg_len,flags統一為0int recv(int socket, char *buffer, int buf_len, int flags) //從對應socket的遠程端接收最大size為buf_len的信息到buffer,flags統一為0關閉socket,釋放資源
int close(int socket)?
客戶端工作流程:
創建socket(方法同上)
向服務端發起連接請求
int connect(int socket, struct sockaddr *address, int addr_len) //socket是被綁定套接字的標識符 //address是包含服務端服務器IP地址和TCP端口號的結構體 //addr_len是address的size //此函數也是個阻塞操作,當建立連接時才返回,返回值是遠程服務器的socket描述符與服務端通信,數據發送完后斷開連接(方法同上)
關閉socket,釋放資源(方法同上)
?
?
C++代碼示例
??編譯環境:Ubuntu 20.04、g++ 9.3.0
Server:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h>int main(int argc, char *argv[]) {if (argc != 2){printf("請輸入端口號!\n\n");return -1;}// 第1步:創建服務端的socket。int listenfd;if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){perror("socket");return -1;}// 第2步:把服務端用于通信的地址和端口綁定到socket上。struct sockaddr_in servaddr; // 服務端地址信息的數據結構。memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET; // 協議族,在socket編程中只能是AF_INET。servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 使用任意ip地址。//servaddr.sin_addr.s_addr = inet_addr("192.168.190.134"); // 使用指定ip地址。servaddr.sin_port = htons(atoi(argv[1])); // 指定通信端口。if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0){perror("bind");close(listenfd);return -1;}// 第3步:把socket設置為監聽模式。if (listen(listenfd, 5) != 0){perror("listen");close(listenfd);return -1;}// 第4步:接受客戶端的連接。int clientfd; // 客戶端的socket。int socklen = sizeof(struct sockaddr_in); // struct sockaddr_in的大小struct sockaddr_in clientaddr; // 客戶端的地址信息。clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, (socklen_t *)&socklen);printf("客戶端(%s)已連接。\n", inet_ntoa(clientaddr.sin_addr));// 第5步:與客戶端通信,接收客戶端發過來的報文后,回復ok。char buffer[1024];while (1){int iret;memset(buffer, 0, sizeof(buffer));if ((iret = recv(clientfd, buffer, sizeof(buffer), 0)) <= 0) // 接收客戶端的請求報文。{printf("通信結束!\n");break;}printf("對方:%s\n", buffer);if (!strcmp(buffer, "拜拜")){printf("\n通信結束!\n");break;}memset(buffer, 0, sizeof(buffer));printf("我:");scanf("%[^\n]", buffer);char c = getchar();if ((iret = send(clientfd, buffer, strlen(buffer), 0)) <= 0) // 向客戶端發送響應結果。{printf("發送失敗,通信結束!\n");break;}if (!strcmp(buffer, "拜拜")){printf("\n通信結束!\n");break;}}// 第6步:關閉socket,釋放資源。close(listenfd);close(clientfd); }?
Client:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h>int main(int argc, char *argv[]) {if (argc != 3){printf("請輸入ip地址和端口!\n\n");return -1;}// 第1步:創建客戶端的socket。int sockfd;if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){perror("socket");return -1;}// 第2步:向服務器發起連接請求。struct hostent *h;if ((h = gethostbyname(argv[1])) == 0) // 指定服務端的ip地址。{printf("gethostbyname failed.\n");close(sockfd);return -1;}struct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(atoi(argv[2])); // 指定服務端的通信端口。memcpy(&servaddr.sin_addr, h->h_addr, h->h_length);if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) // 向服務端發起連接清求。{perror("connect");close(sockfd);return -1;}// 第3步:與服務端通信,發送一個報文后等待回復,然后再發下一個報文。char buffer[1024];int iret;while (1){memset(buffer, 0, sizeof(buffer));printf("我:");scanf("%[^\n]", buffer);char c = getchar();if ((iret = send(sockfd, buffer, strlen(buffer), 0)) <= 0) // 向服務端發送請求報文。{printf("\n發送失敗,通信結束!\n");break;}if (!strcmp(buffer, "拜拜")){printf("\n通信結束!\n");break;}memset(buffer, 0, sizeof(buffer));if ((iret = recv(sockfd, buffer, sizeof(buffer), 0)) <= 0) // 接收服務端的回應報文。{printf("\n接收失敗,通信結束\n");break;}printf("對方:%s\n", buffer);if (!strcmp(buffer, "拜拜")){printf("\n通信結束!\n");break;}}// 第4步:關閉socket,釋放資源。close(sockfd); }?
效果:
Server
Client
總結
以上是生活随笔為你收集整理的【进程通信】Socket的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【编程通识】PlantUML绘制时序图样
- 下一篇: 基于socket的线上聊天框