socketpair用法及用途

來源:互聯網
上載者:User
 

一.概念及用途

一個問題:如何建立全雙工系統管道?

直接的辦法當然是pipe兩次,建立兩組管道,但是有沒有更簡單的呢?

socketpair就可以了,man socketpair:

socketpair - create a pair of connected sockets, The two sockets are indistinguishable,也就是說,用socketpair建立出來的兩個描述符應該是等價的

二.用法

#define DATA1 "test string 1"

#define DATA2 "test string 2"

#include <stdio.h>

#include <string.h>

#include <unistd.h>

#include <sys/socket.h>

#include <sys/un.h>

#include <sys/types.h>

#include <sys/wait.h>

#include <errno.h>

main (  )

{

int sockets[2], child;

char buf[1024];

/* Get the socket pair */

if (   socketpair(AF_UNIX, SOCK_STREAM, 0, sockets ) < 0 ) {

    printf("error %d on socketpair ", errno);

    exit(1);

}

/* create child process */

if ( ( child = fork() ) == -1 ) {

    printf("fork error %d ", errno);

    exit(1);

}

if (child != 0) {  /* this is the parent */

    /* close child's end of socket */

    close( sockets[0]  );

    /* read message from child */

    if ( read(sockets[1], buf, sizeof(buf) ) < 0) {

      printf("error %d reading socket ", errno);

      exit(1);

    }

    printf("parent:-->%s \n", buf);

    /* write message to child */

    if ( write(sockets[1], DATA1, sizeof(DATA1)) < 0 ) {

      printf("error %d writing socket ", errno);

      exit(1);

    }

    /* finished */

    close(sockets[1]);

}

else

 { /* the child */

    /* close parent's end of socket */

    close(sockets[1]);

    /* send message to parent */

    if (write(sockets[0], DATA2, sizeof(DATA1)) < 0) {

      printf("error %d writing socket ", errno);

      exit(1);

    }

    /* get message from parent */

    if (read(sockets[0], buf, sizeof(buf)) < 0) {

      printf("error %d reading socket ", errno);

      exit(1);

    }

    printf("child: -->%s\n ", buf);

    /* finished */

    close(sockets[0]);

}

}

從上面的代碼中,我們可以發現,父進程從sockets[1]中讀寫,子進程從sockets[0]中讀寫,的確是全雙工系統形態。

唯一值得考慮的是為什麼在父子進程中,要分別關閉sockets[0]和sockets[1]呢?

三。一個實現

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <fcntl.h>

#include <stdio.h>

socketpair(af, type, protocol, fd)

int af;

int type;

int protocol;

int fd[2];

{

int listen_socket;

struct sockaddr_in sin[2];

int len;

/* The following is only valid if type == SOCK_STREAM */

if (type != SOCK_STREAM)

   return -1;

/* Create a temporary listen socket; temporary, so any port is good */

listen_socket = socket(af, type, protocol);

if (listen_socket < 0)

   {

   perror("creating listen_socket");

   return -1;

   }

sin[0].sin_family = af;

sin[0].sin_port = 0; /* Use any port number */

sin[0].sin_addr.s_addr = inet_makeaddr(INADDR_ANY, 0);

if (bind(listen_socket, &sin[0], sizeof(sin[0])) < 0)

   {

   perror("bind");

   return -1;

   }

len = sizeof(sin[0]);

/* Read the port number we got, so that our client can connect to it */

if (getsockname(listen_socket, &sin[0], &len) < 0)

   {

   perror("getsockname");

   return -1;

   }

/* Put the listen socket in listening mode */

if (listen(listen_socket, 5) < 0)

   {

   perror("listen");

   return -1;

   }

/* Create the client socket */

fd[1] = socket(af, type, protocol);

if (fd[1] < 0)

   {

   perror("creating client_socket");

   return -1;

   }

/* Put the client socket in non-blocking connecting mode */

fcntl(fd[1], F_SETFL, fcntl(fd[1], F_GETFL, 0) | O_NDELAY);

if (connect(fd[1], &sin[0], sizeof(sin[0])) < 0)

   {

   perror("connect");

   return -1;

   }

/* At the listen-side, accept the incoming connection we generated */

len = sizeof(sin[1]);

if ((fd[0] = accept(listen_socket, &sin[1], &len)) < 0)

   {

   perror("accept");

   return -1;

   }

/* Reset the client socket to blocking mode */

fcntl(fd[1], F_SETFL, fcntl(fd[1], F_GETFL, 0) & ~O_NDELAY);

close(listen_socket);

return 0;

}

採用的是unix domain socket的技術,首先建立一個監聽socket,因為是臨時性的,其綁定的連接埠和Ip地址都可以任意(由系統指定即可), 然後執行listen;接著建立client socket,其描述符為fd[1],執行connect;最後返回服務端,執行accept,返回的描述符就是實際通訊的描述符fd[0]。

四.補充

socketpair還常用於描述符傳遞的處理中。

我們知道父進程在子進程被fork出來之前開啟的檔案描述符是能被子進程繼承下來的,但是一旦子進程已經建立後,父進程開啟的檔案描述符要怎樣才能傳遞給子進程呢?Unix提供相應的技術來滿足這一需求,這就是同一台主機上進程間的檔案描述符傳遞,很美妙而且強大的技術。

想象一下我們試圖實現一個伺服器,接收多個用戶端的串連,我們欲採用多個子進程並發的形式來處理多用戶端的同時串連,這時候我們可能有兩種想法:
1、用戶端每建立一條串連,我們fork出一個子進程負責處理該串連;
2、預先建立一個進程池,用戶端每建立一條連結,伺服器就從該池中選出一個空閑(Idle)子進程來處理該串連。
後者顯然更高效,因為減少了子進程建立的效能損耗,反應的及時性大大增強。這裡恰恰就出現了我們前面提到的問題,所有子進程都是在伺服器Listen到一條串連以前就已經fork出來了,也就是說新的串連描述符子進程是不知道的,需要父進程傳遞給它,它接收到相應的串連描述符後,才能與相應的用戶端進行通訊處理。這裡我們就可以使用'傳遞檔案描述符'的方式來實現。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.