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