进程间通讯之socketpair的使用

 

socketpair的使用

Linux实现了一个源自BSD的socketpair调用,可以实现在同一个文件描述符中进行读写的功能。 
该系统调用能创建一对已连接的UNIX族socket。 
在Linux中,完全可以把这一对socket当成pipe返回的文件描述符一样使用,唯一的区别就是这一对文件描述符中的任何一个都可读和可写,函数原型如下:

#include <sys/types.h>
#include <sys/socket.h>

int socketpair(int domain, int type, int protocol, int sv[2]);

socketpair()函数建立一对匿名的已经连接的套接字,其特性由协议族d、类型type、协议protocol决定,建立的两个套接字描述符会放在sv[0]和sv[1]中。

socketpair函数参数说明: 
第1个参数domain,表示协议族,只能为AF_LOCAL或者AF_UNIX。 
第2个参数type,表示协议,可以是SOCK_STREAM或者SOCK_DGRAM。用SOCK_STREAM建立的套接字对是管道流,与一般的管道相区别的是,套接字对建立的通道是双向的,即每一端都可以进行读写。 
第3个参数protocol,表示类型,只能为0。 
第4个参数sv[2]是接收代表两个套接口的整数数组。每一个文件描述符代表一个套接口,并且与另一个并没有区别

函数返回值: 
如果函数成功,将会返回0值。否则将会返回-1表明创建失败,并且errno来表明特定的错误号。

以下给出个简单的例子,通过socketpair实现父子间进程通信:

/*socketpair1.c*/
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <stdio.h>
 #include <unistd.h>
int main ()
{
    int fd[2];
    int r = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
    if (r < 0){
        perror( "socketpair()" );
        exit(1);
    }

    if (fork()){ /* 父进程 */
        int val = 0;
        close(fd[1]);
        while (1){
            sleep(1);
            ++val;
            printf("%d child->发送数据: %d\n", getpid(), val);
            write(fd[0], &val, sizeof(val));
            read(fd[0], &val, sizeof(val));
            printf("%d child->接收数据: %d\n", getpid(), val);
        }
    }else{  /*子进程*/
        int val;
        close(fd[0]);
        while(1){
            read(fd[1], &val, sizeof(val));
            printf("%d father->接收数据: %d\n", getpid(), val);
            ++val;
            printf("%d father->发送数据: %d\n", getpid(), val);
            write(fd[1], &val, sizeof(val));
        }
    }
}

执行结果:

yu@ubuntu:~/Linux/217/pro_pool/socketpair$ vi socketpair1.c
yu@ubuntu:~/Linux/217/pro_pool/socketpair$ gcc -o socketpair1 socketpair1.c
yu@ubuntu:~/Linux/217/pro_pool/socketpair$ ./socketpair1
5635 child->发送数据: 1
5636 father->接收数据: 1
5636 father->发送数据: 2
5635 child->接收数据: 2
5635 child->发送数据: 3
5636 father->接收数据: 3
5636 father->发送数据: 4
5635 child->接收数据: 4
5635 child->发送数据: 5
5636 father->接收数据: 5
5636 father->发送数据: 6
5635 child->接收数据: 6
5635 child->发送数据: 7
5636 father->接收数据: 7
5636 father->发送数据: 8
5635 child->接收数据: 8
5635 child->发送数据: 9
5636 father->接收数据: 9
5636 father->发送数据: 10
5635 child->接收数据: 10
5635 child->发送数据: 11
5636 father->接收数据: 11
5636 father->发送数据: 12...

分析程序:一开始由socketpair创建一个套接字对,父进程关闭fd[1],子进程关闭fd[0],父进程sleep(1)让子进程先执行,子进程read(fd[1], &val, sizeof(val))阻塞,然后父进程write(fd[0]..)发送数据,子进程接收数据处理后再发送给父进程数据write(fd[1]..),父进程读取数据,打印输出。(注意:socketpair产生的套接字对实现全双工通信


sendmsg, recvmsg , send函数的使用

sendmsg, recvmsg , send三个函数的头文件:

#include <sys/types.h>  
#include <sys/socket.h>
  • 1
  • 2

sendmsg函数 
定义函数

int sendmsg(int s, const strcut msghdr *msg, unsigned int flags);
  • 1

函数说明:sendmsg()用来将数据由指定的socket传给对方主机. 
参数s:为已建立好连线的socket, 如果利用UDP协议则不需经过连线操作. 
参数msg:指向欲连线的数据结构内容, 参数flags 一般默认为0, 详细描述请参考send(). 
返回值:成功返回发送的字节数,出错返回-1

recvmsg函数 
定义函数

int recvmsg(int s, struct msghdr *msg, unsigned int flags);
  • 1

函数说明:recvmsg()用来接收远程主机经指定的socket 传来的数据. 
参数s 为已建立好连线的socket, 如果利用UDP 协议则不需经过连线操作. 
参数msg 指向欲连线的数据结构内容, 
参数flags 一般设0, 详细描述请参考send(). 
返回值:成功则返回接收到的字符数, 失败则返回-1, 错误原因存于errno 中.

send函数 
定义函数:int send(int s, const void * msg, int len, unsigned int falgs); 
函数说明:send()用来将数据由指定的socket 传给对方主机. 
参数s 为已建立好连接的socket. 
参数msg 指向欲连线的数据内容. 
参数len 则为数据长度. 
参数flags 一般设0, 其他数值定义如下: 
MSG_OOB 传送的数据以out-of-band 送出. 
MSG_DONTROUTE 取消路由表查询 
MSG_DONTWAIT 设置为不可阻断运作 
MSG_NOSIGNAL 此动作不愿被SIGPIPE 信号中断. 
返回值:成功则返回实际传送出去的字符数, 失败返回-1. 错误原因存于errno.


结构msghdr定义如下:

struct msghdr
{
    void *msg_name; //发送或接收数据的地址
    socklen_t msg_namelen; //地址长度
    strcut iovec * msg_iov; //要发送或接受数据
    size_t msg_iovlen; //容器数据长度
    void * msg_control; //附属数据
    size_t msg_controllen; //附属数据长度
    int msg_flags; //接收消息的标志
};

返回值:成功则返回实际传送出去的字符数, 失败返回-1, 错误原因存于errno 
错误代码:

1、EBADF 参数s 非合法的socket 处理代码.
2、EFAULT 参数中有一指针指向无法存取的内存空间
3、ENOTSOCK 参数s 为一文件描述词, 非socket.
4、EINTR 被信号所中断.
5、EAGAIN 此操作会令进程阻断, 但参数s 的socket 为不可阻断.
6、ENOBUFS 系统的缓冲内存不足
7、ENOMEM 核心内存不足 EINVAL 传给系统调用的参数不正确.

附属数据msg_control结构 
控制信息头部本身由下面的C结构定义:

struct cmsghdr {
    socklen_t cmsg_len;
    int       cmsg_level;
    int       cmsg_type;
/* u_char     cmsg_data[]; */
};

其成员描述如下:

成员             描述
cmsg_len        附属数据的字节计数,这包含结构头的尺寸。这个值是由CMSG_LEN()宏计算的。
cmsg_level      这个值表明了原始的协议级别(例如,SOL_SOCKET)。
cmsg_type       这个值表明了控制信息类型(例如,SCM_RIGHTS)。
cmsg_data       这个成员并不实际存在,用来指明实际的额外附属数据所在的位置。

用sendmsg来传递数据程序实例

/*sendmsg.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>

int main(int argc,char *argv[])
{
    int ret;     /* 返回值 */
    int sock[2];    /* 套接字对 */
    struct msghdr msg;
    struct iovec iov[1];
    char send_buf[100] = "it is a test";
    struct msghdr msgr;
    struct iovec iovr[1];
    char recv_buf[100];

    /* 创建套接字对 */
    ret = socketpair(AF_LOCAL,SOCK_STREAM,0,sock);
    if(ret == -1){
        printf("socketpair err\n");
        return 1;   
    }

    /* sock[1]发送数据到本地主机  */
    bzero(&msg, sizeof(msg));
    msg.msg_name = NULL;        /* void*类型 NULL本地地址*/
    msg.msg_namelen = 0;
    iov[0].iov_base = send_buf;
    iov[0].iov_len = sizeof(send_buf);
    msg.msg_iov = iov;//要发送或接受数据设为iov
    msg.msg_iovlen = 1;//1个元素

    printf("开始发送数据:\n");
    printf("发送的数据为: %s\n", send_buf);
    ret = sendmsg(sock[1], &msg, 0 );
    if(ret == -1 ){
        printf("sendmsg err\n");
        return -1;
    }
    printf("发送成功!\n");

    /* 通过sock[0]接收发送过来的数据 */
    bzero(&msg, sizeof(msg));
    msgr.msg_name = NULL;   
    msgr.msg_namelen = 0;
    iovr[0].iov_base = &recv_buf;
    iovr[0].iov_len = sizeof(recv_buf);
    msgr.msg_iov = iovr;
    msgr.msg_iovlen = 1;
    ret = recvmsg(sock[0], &msgr, 0);
    if(ret == -1 ){
        printf("recvmsg err\n");
        return -1;
    }
    printf("接收成功!\n");
    printf("收到数据为: %s\n", recv_buf);

    /* 关闭sockets */
    close(sock[0]);
    close(sock[1]);

    return 0;
}

执行程序结果:

yu@ubuntu:~/Linux/217/pro_pool/socketpair$ gcc -o sendmsg sendmsg.c
yu@ubuntu:~/Linux/217/pro_pool/socketpair$ ./sendmsg
开始发送数据:
发送的数据为: it is a test
发送成功!
接收成功!
收到数据为: it is a test

程序分析:由套接字sock[1]发数据到本地主机,由套接字sock[0]接收发送过来的数据。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
进程通信(IPC)是指在操作系统中,不同进程进行信息交流和数据传递的机制。其中一种常用的IPC方式就是使用Socket进行通信。Socket是一种用于实现进程网络通信的编程接口。通过Socket进程可以在不同的计算机之进行数据传输。 在引用中提到了使用LoRa通信时,需要在发送端和接收端使用Socket进行通信。发送端将浮点数值打包为二进制数据,并通过LoRa通信Socket发送。接收端则通过LoRa通信Socket接收数据,并将接收到的二进制数据解包为浮点数进行处理。 引用提到了另一种使用Socket进行进程通信的方案,即Local Socket。这种方案使用TCP协议创建服务端和客户端,实现进程的通信效果。这种方案通常用于解决扩展和容器应用的实时通讯问题。 因此,进程通信可以使用Socket来进行实现,而具体使用Socket类型和通信方式取决于具体的应用场景和需求。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [演示如何使用Python和PyLoRa库在LoRa设备之进行通信](https://download.csdn.net/download/weixin_44609920/88217590)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [进程的实时通讯方案: local socket(解决扩展和容器应用的实时通讯问题)](https://download.csdn.net/download/u011018979/15137188)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

羊族的希望

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值