細說linux IPC(一):基於socket的進程間通訊(上)

    【版權聲明:尊重原創。轉載請保留出處:blog.csdn.net/shallnet 或 .../gentleliu,文章僅供學習交流,請勿用於商業用途】
    在一個較大的project其中。通常都會有多個進程構成,各個功能是一個獨立的進程在執行。

既然多個進程構成一個project,那麼多個進程之間確定會存在一些信息交換或共享數據,這就涉及到進程間通訊。進程間通道有很是多種。比方有最熟悉網絡編程中的socket、還有共享內存、消息隊列、信號、管道等很是多方式。每一種方式都有本身的適用狀況,在本系列文章中筆者將會對多種進程間通訊方式進行具體解釋,一是對本身工做多年在這方面的經驗作一個積累,二是將其分享給各位民工。也許還能從你們的拍磚其中獲得意外收穫。
 
socket網絡編程可能使用得最多,經常常使用在網絡上不一樣主機之間的通訊。linux

事實上在同一主機內通訊也可以使用socket來完畢。socket進程通訊與網絡通訊使用的是統一套接口,不過地址結構與某些參數不一樣。在使用socket建立套接字時經過指定參數domain是af_inet(ipv4因特網域)或af_inet6(ipv6因特網域)或af_unix(unix域)來實現。
在筆者這一篇文章中曾有具體介紹建立socket通訊流程及基礎知識。編程


http://blog.csdn.net/shallnet/article/details/17734919
 
首先來看一下使用af_inet域以及本地環回地址來實現本地主機進程間通訊。
服務進程建立監聽套接字:
服務器

int ser_afinet_listen(int port)
{
    int                 listenfd, on;
    struct sockaddr_in  seraddr;
 
    listenfd = socket(af_inet, sock_stream, 0);
    if (listenfd < 0) {
        fprintf(stderr, "socket: %s\n", strerror(errno));
        return -1;
    }
 
    on = 1;
    setsockopt(listenfd, sol_socket, so_reuseaddr, &on, sizeof(on));
 
    seraddr.sin_family = af_inet;
    seraddr.sin_port = port;
    seraddr.sin_addr.s_addr = htonl(inaddr_any);
 
    if (bind(listenfd, (struct sockaddr *)&seraddr, sizeof(struct sockaddr_in)) < 0) {
        fprintf(stderr, "bind: %s\n", strerror(errno));
        return -1;
    }
 
    if (listen(listenfd, sock_ipc_max_conn) < 0) {
        fprintf(stderr, "listen: %s\n", strerror(errno));
        return -1;
    }
 
    return listenfd;
}

服務進程處理鏈接請求例如如下:
int ser_accept(int listenfd)
{
    int                 connfd;
    struct sockaddr_un  cltaddr;
    ssize_t             recvlen, sendlen;
    char                buf[sock_ipc_max_buf];
    socklen_t           addrlen;
 
    addrlen = sizeof(cltaddr);
    for (;;) {
        connfd = accept(listenfd, (struct sockaddr *)&cltaddr, &addrlen);
        if (connfd < 0) {
            fprintf(stderr, "accept: %s\n", strerror(errno));
            return -1;
        }
 
        if (recvlen = ipc_recv(connfd, buf, sizeof(buf)) < 0) {
            continue;
        }
 
        printf("recv: %s\n", buf);
        snprintf(buf, sizeof(buf), "hello, ipc client!");
        if (ipc_send(connfd, buf, strlen(buf)) < 0) {
            continue;
        }
 
        close(connfd);
    }
}
客戶進程初始化例如如下:
指定要鏈接的服務器地址爲本地換回地址,這樣發送的鏈接就會回到本地服務進程。
int clt_afinet_conn_init(int port)
{
    int                 fd;
 
    fd = socket(af_inet, sock_stream, 0);
    if (fd < 0) {
        fprintf(stderr, "socket: %s\n", strerror(errno));
        return -1;
    }
 
    seraddr.sin_family = af_inet;
    seraddr.sin_port = port;
    if (inet_pton(af_inet, "127.0.0.1", &seraddr.sin_addr) < 0) {//環回地址
        fprintf(stderr, "inet_pton: %s\n", strerror(errno));
        return -1;
    }
 
    return fd;
}

    客戶進程向服務進程發送接收請求例如如下:
if (connect(fd, (struct sockaddr *)&seraddr, sizeof(struct sockaddr_in)) < 0) {
        fprintf(stderr,  "connect: %s\n", strerror(errno));
        return -1;
    }
 
    if ((sendlen = ipc_send(fd, buf, strlen(buf))) < 0) {
        return -1;
    }
 
    if ((recvlen = ipc_recv(fd, buf, sizeof(buf))) < 0) {
        return -1;
    }

服務進程先執行,客戶進程執行:
# ./client
recv: hello, ipc client!
# ./server
recv: hello ipc server!
通訊過程完畢。


建立類型爲af_unix(或af_local)的socket,表示用於進程通訊。網絡


socket進程通訊與網絡通訊使用的是統一套接口:
dom

#include<sys/socket.h>
int socket(int domain, int type, int protocol);
其中,domain 參數指定協議族,對於本地套接字來講,其值被置爲 af_unix 枚舉值,隨便說一下,af_unix和af_local是同一個值,看如下linux/socket.h頭文件部分例如如下,兩個宏的值都同樣爲1。


……
/* supported address families. */
#define af_unspec       0
#define af_unix         1       /* unix domain sockets          */
#define af_local        1       /* posix name for af_unix       */
#define af_inet         2       /* internet ip protocol         */
#define af_ax25         3       /* amateur radio ax.25          */
……
以af_xx開頭和pf_xx開頭的域都是同樣的。繼續看頭文件部份內容就一切都明確了:
……
#define pf_unspec       af_unspec
#define pf_unix         af_unix
#define pf_local        af_local
#define pf_inet         af_inet
#define pf_ax25         af_ax25
……
因此咱們在指定socket的類型時這四個域可以隨便用啦,筆者這裏統一使用af_unix了。


type 參數可被設置爲 sock_stream(流式套接字)或 sock_dgram(數據報式套接字),對於本地套接字來講,流式套接字(sock_stream)是一個有順序的、可靠的雙向字節流,至關於在本地進程之間創建起一條數據通道。數據報式套接字(sock_dgram)至關於單純的發送消息。在進程通訊過程當中。理論上可能會有信息丟失、複製或者不按前後次序到達的狀況,但由於其在本地通訊,不經過外界網絡,這些狀況出現的機率很是小。
本地套接字的通訊兩方均需要具備本地地址,地址類型爲 struct sockaddr_un結構體(位於linux/un.h):
socket

#ifndef _linux_un_h
#define _linux_un_h
 
#define unix_path_max   108
 
struct sockaddr_un {
        sa_family_t sun_family; /* af_unix */
        char sun_path[unix_path_max];   /* pathname */
};
 #endif /* _linux_un_h */
建立監聽套接字:
int ser_afunix_listen(const char *pathname)
{
    int                 listenfd, on;
    struct sockaddr_un  seraddr;
 
    listenfd = socket(af_unix, sock_stream, 0);
    if (listenfd < 0) {
        fprintf(stderr, "socket: %s\n", strerror(errno));
        return -1;
    }
 
    unlink(pathname);
    seraddr.sun_family = af_unix;
    snprintf(seraddr.sun_path, sizeof(seraddr.sun_path), "%s", pathname);
 
    if (bind(listenfd, (struct sockaddr *)&seraddr, sizeof(struct sockaddr_un)) < 0) {
        fprintf(stderr, "bind: %s\n", strerror(errno));
        return -1;
    }
 
    if (listen(listenfd, sock_ipc_max_conn) < 0) {
        fprintf(stderr, "listen: %s\n", strerror(errno));
        return -1;
    }
 
    return listenfd;
}
服務進程處理鏈接請求類似上面網絡通訊。
 
客戶進程初始化套接字過程爲:
int clt_afunix_conn_init(const char *pathname)
{
    int                 fd;
    struct sockaddr_un  localaddr;
 
    fd = socket(af_unix, sock_stream, 0);
    if (fd < 0) {
        fprintf(stderr, "socket: %s\n", strerror(errno));
        return -1;
    }
 
    localaddr.sun_family = af_unix;
    snprintf(localaddr.sun_path, sizeof(localaddr.sun_path), "%s-cltid%d", pathname, getpid());
 
    if (bind(fd, (struct sockaddr *)&localaddr, sizeof(struct sockaddr_un)) < 0) {
        fprintf(stderr, "bind: %s\n", strerror(errno));
        return -1;
    }
 
    seraddr.sun_family = af_unix;
    snprintf(seraddr.sun_path, sizeof(seraddr.sun_path), "%s", pathname);
 
    return fd;
}

客戶進程向服務進程發送接收請求類似上面網絡通訊。
執行結果同網絡通訊部分。
本節只給出了一個很是easy的通訊程序,只簡單實現了客戶進程和服務進程之間的通訊。在下一節筆者將會在本節演示樣例的基礎上作改動,寫一個在實際應用中可以使用的代碼。
本節演示樣例代碼下載連接:
 http://download.csdn.net/detail/gentleliu/8140459
相關文章
相關標籤/搜索