日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux进程间通信方式--本地socket

發布時間:2023/11/30 linux 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux进程间通信方式--本地socket 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

先上一個代碼

服務端:


[cpp] view plaincopy


  • //s_unix.c ?
  • #include <stdio.h> ?
  • #include <sys/types.h> ?
  • #include <sys/socket.h> ?
  • #include <sys/un.h>? ?
  • #define UNIX_DOMAIN "/tmp/UNIX.domain" ?
  • int main(void) ?
  • { ?
  • ? ? socklen_t clt_addr_len; ?
  • ? ? int listen_fd; ?
  • ? ? int com_fd; ?
  • ? ? int ret; ?
  • ? ? int i; ?
  • ? ? static char recv_buf[1024];? ?
  • ? ? int len; ?
  • ? ? struct sockaddr_un clt_addr; ?
  • ? ? struct sockaddr_un srv_addr; ?
  • ? ? listen_fd=socket(PF_UNIX,SOCK_STREAM,0); ?
  • ? ? if(listen_fd<0) ?
  • ? ? { ?
  • ? ? ? ? perror("cannot create communication socket"); ?
  • ? ? ? ? return 1; ?
  • ? ? } ? ?
  • ? ? ?
  • ? ? //set server addr_param ?
  • ? ? srv_addr.sun_family=AF_UNIX; ?
  • ? ? strncpy(srv_addr.sun_path,UNIX_DOMAIN,sizeof(srv_addr.sun_path)-1); ?
  • ? ? unlink(UNIX_DOMAIN); ?
  • ? ? //bind sockfd & addr ?
  • ? ? ret=bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr)); ?
  • ? ? if(ret==-1) ?
  • ? ? { ?
  • ? ? ? ? perror("cannot bind server socket"); ?
  • ? ? ? ? close(listen_fd); ?
  • ? ? ? ? unlink(UNIX_DOMAIN); ?
  • ? ? ? ? return 1; ?
  • ? ? } ?
  • ? ? //listen sockfd? ?
  • ? ? ret=listen(listen_fd,1); ?
  • ? ? if(ret==-1) ?
  • ? ? { ?
  • ? ? ? ? perror("cannot listen the client connect request"); ?
  • ? ? ? ? close(listen_fd); ?
  • ? ? ? ? unlink(UNIX_DOMAIN); ?
  • ? ? ? ? return 1; ?
  • ? ? } ?
  • ? ? //have connect request use accept ?
  • ? ? len=sizeof(clt_addr); ?
  • ? ? com_fd=accept(listen_fd,(struct sockaddr*)&clt_addr,&len); ?
  • ? ? if(com_fd<0) ?
  • ? ? { ?
  • ? ? ? ? perror("cannot accept client connect request"); ?
  • ? ? ? ? close(listen_fd); ?
  • ? ? ? ? unlink(UNIX_DOMAIN); ?
  • ? ? ? ? return 1; ?
  • ? ? } ?
  • ? ? //read and printf sent client info ?
  • ? ? printf("/n=====info=====/n"); ?
  • ? ? for(i=0;i<4;i++) ?
  • ? ? { ?
  • ? ? ? ? memset(recv_buf,0,1024); ?
  • ? ? ? ? int num=read(com_fd,recv_buf,sizeof(recv_buf)); ?
  • ? ? ? ? printf("Message from client (%d)) :%s/n",num,recv_buf); ? ?
  • ? ? } ?
  • ? ? close(com_fd); ?
  • ? ? close(listen_fd); ?
  • ? ? unlink(UNIX_DOMAIN); ?
  • ? ? return 0; ?
  • } ?

  • 客戶端:



    [cpp] view plaincopy


  • //c_unix.c ?
  • #include <stdio.h> ?
  • #include <sys/types.h> ?
  • #include <sys/socket.h> ?
  • #include <sys/un.h> ?
  • #define UNIX_DOMAIN "/tmp/UNIX.domain" ?
  • int main(void) ?
  • { ?
  • ? ? int connect_fd; ?
  • ? ? int ret; ?
  • ? ? char snd_buf[1024]; ?
  • ? ? int i; ?
  • ? ? static struct sockaddr_un srv_addr; ?
  • //creat unix socket ?
  • ? ? connect_fd=socket(PF_UNIX,SOCK_STREAM,0); ?
  • ? ? if(connect_fd<0) ?
  • ? ? { ?
  • ? ? ? ? perror("cannot create communication socket"); ?
  • ? ? ? ? return 1; ?
  • ? ? }? ? ?
  • ? ? srv_addr.sun_family=AF_UNIX; ?
  • ? ? strcpy(srv_addr.sun_path,UNIX_DOMAIN); ?
  • //connect server ?
  • ? ? ret=connect(connect_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr)); ?
  • ? ? if(ret==-1) ?
  • ? ? { ?
  • ? ? ? ? perror("cannot connect to the server"); ?
  • ? ? ? ? close(connect_fd); ?
  • ? ? ? ? return 1; ?
  • ? ? } ?
  • ? ? memset(snd_buf,0,1024); ?
  • ? ? strcpy(snd_buf,"message from client"); ?
  • //send info server ?
  • ? ? for(i=0;i<4;i++) ?
  • ? ? ? ? write(connect_fd,snd_buf,sizeof(snd_buf)); ?
  • ? ? close(connect_fd); ?
  • ? ? return 0; ?
  • } ?

  • 使用套接字除了可以實現網絡間不同主機間的通信外,還可以實現同一主機的不同進程間的通信,且建立的通信是雙向的通信。socket進程通信與網絡通信使用的是統一套接口,只是地址結構與某些參數不同。

    一、創建socket流程

    1)創建socket,類型為AF_LOCALAF_UNIX,表示用于進程通信:

    創建套接字需要使用 socket 系統調用,其原型如下:


    int socket(int domain, int type, int protocol);


    其中,domain 參數指定協議族,對于本地套接字來說,其值須被置為 AF_UNIX 枚舉值;type 參數指定套接字類型,protocol 參數指定具體協議;type 參數可被設置為 SOCK_STREAM(流式套接字)或 SOCK_DGRAM(數據報式套接字),protocol 字段應被設置為 0;其返回值為生成的套接字描述符。


    對于本地套接字來說,流式套接字(SOCK_STREAM)是一個有順序的、可靠的雙向字節流,相當于在本地進程之間建立起一條數據通道;數據報式套接字(SOCK_DGRAM)相當于單純的發送消息,在進程通信過程中,理論上可能會有信息丟失、復制或者不按先后次序到達的情況,但由于其在本地通信,不通過外界網絡,這些情況出現的概率很小。


    二、命名socket

    SOCK_STREAM 式本地套接字的通信雙方均需要具有本地地址,其中服務器端的本地地址需要明確指定,指定方法是使用 struct sockaddr_un 類型的變量。

    struct sockaddr_un {
    ? ? sa_family_t ? ? sun_family; ? ? /* AF_UNIX */
    ? ? char? ? sun_path[UNIX_PATH_MAX];? ? ? ? /* 路徑名 */
    };

    這里面有一個很關鍵的東西,socket進程通信命名方式有兩種。一是普通的命名,socket會根據此命名創建一個同名的socket文件,客戶端連接的時候通過讀取該socket文件連接到socket服務端。這種方式的弊端是服務端必須對socket文件的路徑具備寫權限,客戶端必須知道socket文件路徑,且必須對該路徑有讀權限。

    另外一種命名方式是抽象命名空間,這種方式不需要創建socket文件,只需要命名一個全局名字,即可讓客戶端根據此名字進行連接。后者的實現過程與前者的差別是,后者在對地址結構成員sun_path數組賦值的時候,必須把第一個字節置0,即sun_path[0] = 0,下面用代碼說明:


    第一種方式:

    [cpp] view plaincopy


  • //name the server socket? ?
  • ? ? server_addr.sun_family = AF_UNIX; ?
  • ? ? strcpy(server_addr.sun_path,"/tmp/UNIX.domain"); ?
  • ? ? server_len = sizeof(struct sockaddr_un); ?
  • ? ? client_len = server_len; ?



  • 第二種方式:



    [cpp] view plaincopy


  • #define SERVER_NAME @socket_server ?

  • [cpp] view plaincopy


  • //name the socket ?
  • ? server_addr.sun_family = AF_UNIX; ?
  • ? strcpy(server_addr.sun_path, SERVER_NAME); ?
  • ? server_addr.sun_path[0]=0; ?
  • ? //server_len = sizeof(server_addr); ?
  • ? server_len = strlen(SERVER_NAME)? + offsetof(struct sockaddr_un, sun_path); ?



  • 其中,offsetof函數在#include <stddef.h>頭文件中定義。因第二種方式的首字節置0,我們可以在命名字符串SERVER_NAME前添加一個占位字符串,例如:

    [cpp] view plaincopy


  • #define SERVER_NAME @socket_server ? ?

  • 前面的@符號就表示占位符,不算為實際名稱。


    提示:客戶端連接服務器的時候,必須與服務端的命名方式相同,即如果服務端是普通命名方式,客戶端的地址也必須是普通命名方式;如果服務端是抽象命名方式,客戶端的地址也必須是抽象命名方式。


    三、綁定

    SOCK_STREAM 式本地套接字的通信雙方均需要具有本地地址,其中服務器端的本地地址需要明確指定,指定方法是使用 struct sockaddr_un 類型的變量,將相應字段賦值,再將其綁定在創建的服務器套接字上,綁定要使用 bind 系統調用,其原形如下:


    int bind(int socket, const struct sockaddr *address, size_t address_len);


    其中 socket表示服務器端的套接字描述符,address 表示需要綁定的本地地址,是一個 struct sockaddr_un 類型的變量,address_len 表示該本地地址的字節長度。實現服務器端地址指定功能的代碼如下(假設服務器端已經通過上文所述的 socket 系統調用創建了套接字,server_sockfd 為其套接字描述符):
    struct sockaddr_un server_address;
    server_address.sun_family = AF_UNIX;
    strcpy(server_address.sun_path, "Server Socket");
    bind(server_sockfd, (struct sockaddr*)&server_address, sizeof(server_address));


    客戶端的本地地址不用顯式指定,只需能連接到服務器端即可,因此,客戶端的 struct sockaddr_un 類型變量需要根據服務器的設置情況來設置,代碼如下(假設客戶端已經通過上文所述的 socket 系統調用創建了套接字,client_sockfd 為其套接字描述符):
    struct sockaddr_un client_address;
    client_address.sun_family = AF_UNIX;
    strcpy(client_address.sun_path, "Server Socket");

    四、監聽
    服務器端套接字創建完畢并賦予本地地址值(名稱,本例中為Server Socket)后,需要進行監聽,等待客戶端連接并處理請求,監聽使用 listen 系統調用,接受客戶端連接使用accept系統調用,它們的原形如下:
    int listen(int socket, int backlog);
    int accept(int socket, struct sockaddr *address, size_t *address_len);
    其中 socket 表示服務器端的套接字描述符;backlog 表示排隊連接隊列的長度(若有多個客戶端同時連接,則需要進行排隊);address 表示當前連接客戶端的本地地址,該參數為輸出參數,是客戶端傳遞過來的關于自身的信息;address_len 表示當前連接客戶端本地地址的字節長度,這個參數既是輸入參數,又是輸出參數。實現監聽、接受和處理的代碼如下:
    #define MAX_CONNECTION_NUMBER 10
    int server_client_length, server_client_sockfd;
    struct sockaddr_un server_client_address;
    listen(server_sockfd, MAX_CONNECTION_NUMBER);
    while(1)
    {
    ? ? // ...... (some process code)
    ? ? server_client_length = sizeof(server_client_address);
    ? ? server_client_sockfd = accept(server_sockfd, (struct sockaddr*)server_client_address, &server_client_length);
    ? ? // ...... (some process code)
    }
    這里使用死循環的原因是服務器是一個不斷提供服務的實體,它需要不間斷的進行監聽、接受并處理連接,本例中,每個連接只能進行串行處理,即一個連接處理完后,才能進行后續連接的處理。如果想要多個連接并發處理,則需要創建線程,將每個連接交給相應的線程并發處理。
    客戶端套接字創建完畢并賦予本地地址值后,需要連接到服務器端進行通信,讓服務器端為其提供處理服務。對于 SOCK_STREAM 類型的流式套接字,需要客戶端與服務器之間進行連接方可使用。連接要使用 connect 系統調用,其原形為


    int connect(int socket, const struct sockaddr *address, size_t address_len);


    其中socket為客戶端的套接字描述符,address表示當前客戶端的本地地址,是一個 struct sockaddr_un 類型的變量,address_len 表示本地地址的字節長度。實現連接的代碼如下:
    connect(client_sockfd, (struct sockaddr*)&client_address, sizeof(client_address));
    無論客戶端還是服務器,都要和對方進行數據上的交互,這種交互也正是我們進程通信的主題。一個進程扮演客戶端的角色,另外一個進程扮演服務器的角色,兩個進程之間相互發送接收數據,這就是基于本地套接字的進程通信。發送和接收數據要使用 write read 系統調用,它們的原形為:
    int read(int socket, char *buffer, size_t len);
    int write(int socket, char *buffer, size_t len);
    其中 socket 為套接字描述符;len 為需要發送或需要接收的數據長度;對于 read 系統調用,buffer 是用來存放接收數據的緩沖區,即接收來的數據存入其中,是一個輸出參數;對于 write 系統調用,buffer 用來存放需要發送出去的數據,即 buffer 內的數據被發送出去,是一個輸入參數;返回值為已經發送或接收的數據長度。例如客戶端要發送一個 "Hello" 字符串給服務器,則代碼如下:
    char buffer[10] = "Hello";
    write(client_sockfd, buffer, strlen(buffer));
    交互完成后,需要將連接斷開以節省資源,使用close系統調用,其原形為:
    int close(int socket);
    不多說了,直接使用,大家一定都會,呵呵!
    上面所述的每個系統調用都有 -1 返回值,在調用不成功時,它們均會返回 -1,這個特性可以使得我們用 if - else 或異常處理語句來處理錯誤,為我們提供了很大的方便。
    SOCK_DGRAM
    數據報式本地套接字的應用場合很少,因為流式套接字在本地的連接時間可以忽略,所以效率并沒有提高,而且發送接收都需要攜帶對方的本地地址,因此很少甚至幾乎不使用。
    與本地套接字相對應的是網絡套接字,可以用于在網絡上傳送數據,換言之,可實現不同機器上的進程通信過程。在 TCP/IP 協議中,IP 地址的首字節為 127 即代表本地,因此本地套接字通信可以使用 IP 地址為 127.x.x.x 的網絡套接字來實現。

    總結

    以上是生活随笔為你收集整理的Linux进程间通信方式--本地socket的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。