Linux進程間通信――使用數據報套接字
睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接
前一篇文章,Linux進程間通信——使用流套接字介紹了一些有關socket(套接字)的一些基本內容,并講解了流套接字的使用,這篇文章將會給大家講講,數據報套接字的使用。
一、簡單回顧——什么是數據報套接字。
socket,即套接字是一種通信機制,憑借這種機制,客戶/服務器(即要進行通信的進程)系統的開發工作既可以在本地單機上進行,也可以跨網絡進行。也就是說它可以讓不在同一臺計算機但通過網絡連接計算機上的進程進行通信。也因為這樣,套接字明確地將客戶端和服務器區分開來。
相對于流套接字,數據報套接字的使用更為簡單,它是由類型SOCK_DGRAM指定的,它不需要建立連接和維持一個連接,它們在AF_INET中通常是通過UDP/IP協議實現的。它對可以發送的數據的長度有限制,數據報作為一個單獨的網絡消息被傳輸,它可能會丟失、復制或錯亂到達,UDP不是一個可靠的協議,但是它的速度比較高,因為它并一需要總是要建立和維持一個連接。
二、基于流套接字的客戶/服務器的工作流程
使用數據報socket進行進程通信的進程采用的客戶/服務器系統是如何工作的呢?
1、服務器端
與使用流套接字一樣,首先服務器應用程序用系統調用socket來創建一個套接安,它是系統分配給該服務器進程的類似文件描述符的資源,它不能與其他的進程共享。
接下來,服務器進程會給套接字起個名字(監聽),我們使用系統調用bind來給套接字命名。然后服務器進程就開始等待客戶連接到這個套接字。
不同的是,然后系統調用recvfrom來接收來自客戶程序發送過來的數據。服務器程序對數據進行相應的處理,再通過系統調用sendto把處理后的數據發送回客戶程序。
與流套接字程序相比:
1、在流套接字中的程序中,接收數據是通過系統調用read,而發送數據是通過系統調用write來實現,而在數據報套接字程序中,這是通過recvfrom和sendto調用來實現的。
2、使用數據報套接字的服務器程序并不需要listen調用來創建一個隊列來存儲連接,也不需要accept調用來接收連接并創建一個新的socket描述符
2、客戶端
基于數據報socket的客戶端比服務器端簡單,同樣,客戶應用程序首先調用socket來創建一個未命名的套接字,與服務器一樣,客戶也是通過sendto和recvfrom來向服務器發送數據和從服務器程序接收數據。
與流套接字程序相比:
使用數據報套接字的客戶程序并不需要使用connect系統調用來連接服務器程序,它只要在需要時向服務器所監聽的IP端口發送信息和接收從服務器發送回來的數據即可。
三、數據報socket的接口及作用
socket的接口函數聲明在頭文件sys/types.h和sys/socket.h中。
1、創建套接字——socket系統調用
該函數用來創建一個套接字,并返回一個描述符,該描述符可以用來訪問該套接字,它的原型如下:
[cpp] view plaincopyprint?
再運行三個客戶端:如下:
在本例子中,我們啟動了一個服務器程序和三個客戶程序,從運行的結果來看,客戶端發送給服務器程序的所有請求都得到了處理,即把大寫字母變成了小寫。recvfrom調用是阻塞的調用,即只有當接收到數據才會返回。
五、數據報套接字與流套接字的比較
1、從使用的便利和效率來講
我們可以看到使用數據報套接字的確是比使用流套接字簡單,而且快速。
因為使用流套接字的程序,客戶程序需要調用connect來創建一個到服務器程序的連接,并需要維持這個連接,服務器程序也需要調用listen來創建一個隊列來保存未處理的請求,當有數據到達時,服務器也不需要調用accept來接受連接并創建一個新socket描述符來處理請求。
再來看看使用數據報套接字的程序,服務器程序與客戶程序所使用的系統調用大致相同,服務器程序只比客戶程序多使用了一個bind調用。基于數據報套接字的程序,只需要使用sendto調用來向指定IP端口的程序發送信息,使用recvfrom調用從指向的IP端口接收信息即可。因為它并不需要建立一個連接,接受連接等,所以省去了很多的功夫。
2、從使用場合來講
我們知道流套接字是基于TCP/IP協議的,它是一種安全的協議,提供的是一個有序、可靠、雙向字節流的連接,發送的數據可以確保不會丟失、重復或亂序到達,而且它還有一定的出錯后重新發送的機制。所以它比較適合用來發送信息量大的數據文件,或對數據完整性要求較高的文件,如壓縮文件、視頻文件等
而數據報套接字是基于UDP/IP協議實現的。它對可以發送的數據的長度有限制,數據報作為一個單獨的網絡消息被傳輸,它可能會丟失、復制或錯亂到達,UDP不是一個可靠的協議,但是它的速度比較高。所以它比較適合發送一些對實時性要求較高,但是對安全性和完整性要求不太高的數據。如我們熟悉的聊天信息,即使有一點的丟失也不會造成理解上的大的問題。RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
- int socket(int domain, int type, int protocol);
- int bind( int socket, const struct sockaddr *address, size_t address_len);
- int sendto(int sockfd, void *buffer, size_t len, int flags, struct sockaddr *to, socklen_t tolen);
- int recvfrom(int sockfd, void *buffer, size_t len,int flags, struct sockaddr *src_from, socklen_t *src_len);
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <signal.h>
- int main()
- {
- int server_sockfd = -1;
- int server_len = 0;
- int client_len = 0;
- char buffer[512];
- int result = 0;
- struct sockaddr_in server_addr;
- struct sockaddr_in client_addr;
- //創建數據報套接字
- server_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- //設置監聽IP端口
- server_addr.sin_family = AF_INET;
- server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
- server_addr.sin_port = htons(9739);
- server_len = sizeof(server_addr);
- //綁定(命名)套接字
- bind(server_sockfd, (struct sockaddr*)&server_addr, server_len);
- //忽略子進程停止或退出信號
- signal(SIGCHLD, SIG_IGN);
- while(1)
- {
- //接收數據,用client_addr來儲存數據來源程序的IP端口
- result = recvfrom(server_sockfd, buffer, sizeof(buffer), 0,
- (struct sockaddr*)&client_addr, &client_len);
- if(fork() == 0)
- {
- //利用子進程來處理數據
- buffer[0] += 'a' - 'A';
- sleep(5);
- //發送處理后的數據
- sendto(server_sockfd, buffer, sizeof(buffer),0 ,
- (struct sockaddr*)&client_addr, client_len);
- printf("%c\n", buffer[0]);
- //注意,一定要關閉子進程,否則程序運行會不正常
- exit(0);
- }
- }
- //關閉套接字
- close(server_sockfd);
- }
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <stdlib.h>
- #include <stdio.h>
- int main(int agrc, char *argv[])
- {
- struct sockaddr_in server_addr;
- int server_len = 0;
- int sockfd = -1;
- int result = 0;
- char c = 'A';
- //取第一個參數的第一個字符
- if(agrc > 1)
- c = argv[1][0];
- //創建數據報套接字
- sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- //設置服務器IP端口
- server_addr.sin_family = AF_INET;
- server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- server_addr.sin_port = htons(9739);
- server_len = sizeof(server_addr);
- //向服務器發送數據
- sendto(sockfd, &c, sizeof(char), 0,
- (struct sockaddr*)&server_addr, server_len);
- //接收服務器處理后發送過來的數據,由于不關心數據來源,所以把后兩個參數設為0
- recvfrom(sockfd, &c, sizeof(char), 0, 0, 0);
- printf("char from server = %c\n", c);
- //關閉套接字
- close(sockfd);
- exit(0);
- }

