1. 程式人生 > >Llinux程序間通訊-AF_UNIX 套接字程式設計

Llinux程序間通訊-AF_UNIX 套接字程式設計

AF_UNIX 地址系列(使用 AF_UNIX 或 AF_UNIX_CCSID 地址系列的套接字)可以是面向連線的(型別 SOCK_STREAM),也可以是無連線的(型別 SOCK_DGRAM)。兩種型別都很可靠,原因是沒有連線兩個程序的外部通訊函式。

UNIX 域資料報套接字的執行方式與 UDP 資料報套接字有所不同。藉助 UDP 資料報套接字,客戶機程式就不必呼叫 bind() 函式,原因是系統會自動指定未使用的埠號。於是伺服器可將資料報傳送回該埠號。但是,使用 UNIX 域資料報套接字,系統不會自動指定客戶機的路徑名。因此,使用 UNIX 域資料報的所有客戶機程式必須呼叫bind() 函式。在客戶機的 bind()

上指定的精確路徑名就是傳遞至伺服器的路徑名。因此,如果客戶機指定相對路徑名(即,並非以 / 開頭的全限定路徑名),除非伺服器以同一當前目錄執行,否則它不能向客戶機發送資料報。

應用程式可能對此地址系列使用的示例路徑名就是 /tmp/myserver 或 servers/thatserver。藉助 servers/thatserver,可使用並非全限定(未指定 /)的路徑名。這表示該項在檔案系統層次結構中的位置應根據當前工作目錄確定。

注意:
檔案系統中的路徑名是啟用了 NLS 的。

下圖舉例說明了 AF_UNIX 地址系列的客戶機/伺服器關係。有關將環境設定為使用 AF_UNIX 地址系列的詳細資訊,參見

套接字程式設計的先決條件


在伺服器和客戶機 AF_UNIX 地址系列示例程式中使用的套接字事件流。

套接字事件流:使用 AF_UNIX 地址系列的伺服器應用程式
示例:使用 AF_UNIX 地址系列的伺服器應用程式使用以下函式呼叫序列:

  1. 函式返回表示端點的套接字描述符。該語句還標識將對此套接字使用帶有流傳輸(SOCK_STREAM)的 UNIX 地址系列。該函式返回表示端點的套接字描述符。還可使用 函式初始化 UNIX 套接字。

    AF_UNIX 或 AF_UNIX_CCSID 是支援 socketpair() 函式的唯一地址系列。socketpair() 函式返回未命名的和已連線的套接字描述符。

  2. 在建立套接字描述符之後, 函式獲取套接字的唯一名稱。

    UNIX 域套接字的名稱空間由路徑名組成。當套接字程式呼叫 bind()

    函式時,會在檔案系統目錄中建立一項。如果路徑名已存在,則bind() 失敗。因此,UNIX 域套接字程式應總是呼叫 函式以在結束時除去該目錄項。

  3. 允許伺服器接受入局客戶機連線。在此示例中,儲備設定為 10。這表示系統將對 10 個入局連線請求排隊,然後才開始拒絕入局請求。
  4. 函式從客戶機應用程式接收資料。在此示例中,我們知道客戶機將傳送超過 250 位元組的資料。既然如此,就可以使用 SO_RCVLOWAT 套接字選項指定在所有 250 位元組資料都到達之前不要喚醒recv()
  5. 函式將資料回傳至客戶機。
  6. 函式關閉所有開啟的套接字描述符。
  7. 函式從檔案系統除去 UNIX 路徑名。

套接字事件流:使用 AF_UNIX 地址系列的客戶機應用程式
示例:使用 AF_UNIX 地址系列的客戶機應用程式使用以下函式呼叫序列:

  1. 函式返回表示端點的套接字描述符。該語句還標識將對此套接字使用帶有流傳輸(SOCK_STREAM)的 UNIX 地址系列。該函式返回表示端點的套接字描述符。還可使用 函式初始化 UNIX 套接字。

    AF_UNIX 或 AF_UNIX_CCSID 是支援 socketpair() 函式的唯一地址系列。socketpair() 函式返回未命名的和已連線的套接字描述符。

  2. 接收到套接字描述符後,使用 函式來建立與伺服器的連線。
  3. 函式傳送指定的 250 位元組資料,該資料是在伺服器應用程式中使用 SO_RCVLOWAT 套接字選項指定的。
  4. 函式一直迴圈,直到所有 250 位元組資料都到達為止。
  5. 函式關閉所有開啟的套接字描述符。

程式說明:
程式裡包含服務端和客戶端兩個程式,它們之間使用 AF_UNIX 實現本機資料流通訊。使用 AF_UNIX 域實際上是使用本地 socket 檔案來通訊。

伺服器端程式碼:
引用

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>

int main (int argc, char *argv[])
{
int server_sockfd, client_sockfd;
int server_len, client_len;
struct sockaddr_un server_address; /*宣告一個UNIX域套接字結構*/
struct sockaddr_un client_address;
int i, bytes;
char ch_send, ch_recv;

unlink ("server_socket"); /*刪除原有server_socket物件*/

/*建立 socket, 通訊協議為AF_UNIX, SCK_STREAM 資料方式*/
server_sockfd = socket (AF_UNIX, SOCK_STREAM, 0);

/*配置伺服器資訊(通訊協議)*/
server_address.sun_family = AF_UNIX;

/*配置伺服器資訊(socket 物件)*/
strcpy (server_address.sun_path, "server_socket");

/*配置伺服器資訊(伺服器地址長度)*/
server_len = sizeof (server_address);

/*繫結 socket 物件*/
bind (server_sockfd, (struct sockaddr *)&server_address, server_len);

/*監聽網路,佇列數為5*/
listen (server_sockfd, 5);

printf ("Server is waiting for client connect...\n");

client_len = sizeof (client_address);

/*接受客戶端請求; 第2個引數用來儲存客戶端地址; 第3個引數用來儲存客戶端地址的大小*/
/*建立(返回)一個到客戶端的檔案描述符,用以對客戶端的讀寫操作*/
client_sockfd = accept (server_sockfd, (struct sockaddr *)&server_address, (socklen_t *)&client_len);
if (client_sockfd == -1) {
perror ("accept");
exit (EXIT_FAILURE);
}

printf ("The server is waiting for client data...\n");

for (i = 0, ch_send = '1'; i < 5; i++, ch_send++) {
if ((bytes = read (client_sockfd, &ch_recv, 1)) == -1) {
perror ("read");
exit (EXIT_FAILURE);
}

printf ("The character receiver from client is %c\n", ch_recv);

sleep (1);

if ((bytes = write (client_sockfd, &ch_send, 1)) == -1) {
perror ("read");
exit (EXIT_FAILURE);
}
}

close (client_sockfd);
unlink ("server socket");
}



客戶端程式碼:
引用
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>

int main (int argc, char *argv[])
{
struct sockaddr_un address;
int sockfd;
int len;
int i, bytes;
int result;
char ch_recv, ch_send;

/*建立socket,AF_UNIX通訊協議,SOCK_STREAM資料方式*/
if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror ("socket");
exit (EXIT_FAILURE);
}

address.sun_family = AF_UNIX;
strcpy (address.sun_path, "server_socket");
len = sizeof (address);

/*向伺服器傳送連線請求*/
result = connect (sockfd, (struct sockaddr *)&address, len);
if (result == -1) {
printf ("ensure the server is up\n");
perror ("connect");
exit (EXIT_FAILURE);
}

for (i = 0, ch_send = 'A'; i < 5; i++, ch_send++) {
if ((bytes = write(sockfd, &ch_send, 1)) == -1) { /*發訊息給伺服器*/
perror ("write");
exit (EXIT_FAILURE);
}

sleep (2); /*休息二秒鐘再發一次*/

if ((bytes = read (sockfd, &ch_recv, 1)) == -1) { /*接收訊息*/
perror ("read");
exit (EXIT_FAILURE);
}

printf ("receive from server data is %c\n", ch_recv);
}
close (sockfd);

return (0);
}



程式說明:
先執行伺服器端,然後再執行客戶端可以在兩邊同時看到輸出。伺服器端先執行後會出現如下提示:
引用
./sock_local_server
Server is waiting for client connect...

這表示,伺服器端已經被阻塞到到 accept() 這裡,伺服器就在此等候客戶端的連線。
如果不是先執行伺服器端,而直接執行客戶端,那麼客戶端會提示:
引用
./sock_local_client
ensure the server is up
connect: Connection refused

提示伺服器沒有準備好,連線被拒絕,從而直接退出程式。

如果伺服器和客戶端依次執行,可以在兩邊看到輸出:
伺服器端:
引用
./sock_local_server
Server is waiting for client connect...
The server is waiting for client data...
The character receiver from client is A
The character receiver from client is B
The character receiver from client is C
The character receiver from client is D
The character receiver from client is E

客戶端:
引用
./sock_local_client
receive from server data is 1
receive from server data is 2
receive from server data is 3
receive from server data is 4
receive from server data is 5