C语言 socketpair用法案例讲解
作者:雪过无痕_ 发布时间:2022-07-24 12:41:48
socketpair()函数的声明:
#include <sys/types.h>
#include <sys/socket.h>
int socketpair(int d, int type, int protocol, int sv[2]);
socketpair()函数用于创建一对无名的、相互连接的套接子。
如果函数成功,则返回0,创建好的套接字分别是sv[0]和sv[1];否则返回-1,错误码保存于errno中。
基本用法:
这对套接字可以用于全双工通信,每一个套接字既可以读也可以写。例如,可以往sv[0]中写,从sv[1]中读;或者从sv[1]中写,从sv[0]中读;
如果往一个套接字(如sv[0])中写入后,再从该套接字读时会阻塞,只能在另一个套接字中(sv[1])上读成功;
读、写操作可以位于同一个进程,也可以分别位于不同的进程,如父子进程。如果是父子进程时,一般会功能分离,一个进程用来读,一个用来写。因为文件描述副sv[0]和sv[1]是进程共享的,所以读的进程要关闭写描述符, 反之,写的进程关闭读描述符。
举例:
一、读写操作位于同一进程
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <error.h>
#include <errno.h>
#include <sys/socket.h>
#include <stdlib.h>
const char* str = "SOCKET PAIR TEST.";
int main(int argc, char* argv[]){
char buf[128] = {0};
int socket_pair[2];
pid_t pid;
if(socketpair(AF_UNIX, SOCK_STREAM, 0, socket_pair) == -1 ) {
printf("Error, socketpair create failed, errno(%d): %s\n", errno, strerror(errno));
return EXIT_FAILURE;
}
int size = write(socket_pair[0], str, strlen(str));
//可以读取成功;
read(socket_pair[1], buf, size);
printf("Read result: %s\n",buf);
return EXIT_SUCCESS;
}
二、读写操作位于不同进程
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <error.h>
#include <errno.h>
#include <sys/socket.h>
#include <stdlib.h>
const char* str = "SOCKET PAIR TEST.";
int main(int argc, char* argv[]){
char buf[128] = {0};
int socket_pair[2];
pid_t pid;
if(socketpair(AF_UNIX, SOCK_STREAM, 0, socket_pair) == -1 ) {
printf("Error, socketpair create failed, errno(%d): %s\n", errno, strerror(errno));
return EXIT_FAILURE;
}
pid = fork();
if(pid < 0) {
printf("Error, fork failed, errno(%d): %s\n", errno, strerror(errno));
return EXIT_FAILURE;
} else if(pid > 0) {
//关闭另外一个套接字
close(socket_pair[1]);
int size = write(socket_pair[0], str, strlen(str));
printf("Write success, pid: %d\n", getpid());
} else if(pid == 0) {
//关闭另外一个套接字
close(socket_pair[0]);
read(socket_pair[1], buf, sizeof(buf));
printf("Read result: %s, pid: %d\n",buf, getpid());
}
for(;;) {
sleep(1);
}
return EXIT_SUCCESS;
}
sendmsg, recvmsg , send函数的使用
sendmsg, recvmsg , send三个函数的头文件:
#include <sys/types.h>
#include <sys/socket.h>
sendmsg函数
定义函数
int sendmsg(int s, const strcut msghdr *msg, unsigned int flags);
函数说明:sendmsg()用来将数据由指定的socket传给对方主机.
参数s:为已建立好连线的socket, 如果利用UDP协议则不需经过连线操作.
参数msg:指向欲连线的数据结构内容, 参数flags 一般默认为0, 详细描述请参考send().
返回值:成功返回发送的字节数,出错返回-1
recvmsg函数
定义函数
int recvmsg(int s, struct msghdr *msg, unsigned int flags);
函数说明: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]接收发送过来的数据。
来源:https://blog.csdn.net/weixin_40039738/article/details/81095013
猜你喜欢
- 前言本文讲解了在Spring 应用中创建Bean的多种方式,包括自动创建,以及手动创建注入方式,实际开发中可以根据业务场景选择合适的方案。方
- Maven多模块编译慢最近在部署项目时发现,Maven编译打包相当耗时,比之前项目用Gradle慢了很多倍,特别是对于WEB工程,打war包
- 用AndroidStudio编写高级计算器带三角函数对数运算功能界面效果图:layout布局 activity_jisuanqi.xml代码
- 引言在平时的工作中多多少少都会接触到注册中心,当你的应用从单机到拆分成多个服务,每个服务又有多个实例的情况时,那么对服务IP地址管理的要求就
- 要想使Java运行,我们可以设计一个面向Java语言特性的虚拟机,并通过编译器将Java程序转换为它可以识别的指令序列,也称为Java字节码
- 经常有同学问到,使用Android能不能开发游戏呢?能开发那些游戏呢?由于操作系统和开发语言局限,一般开发安卓手机游戏,我们很少使用其自带语
- 简介DataBinding 是 Jetpack 组件之一,适用于 MVVM 模式开发,也是Google官方推荐使用的组件之一。使用DataB
- Maven 组件界面介绍 如上图标注 1 所示,为常用的 Maven 工具栏,其中最常用的有:第一个按钮:Reimport All
- Redisson是架设在redis基础上的一个Java驻内存数据网格(In-Memory Data Grid)。充分的利用了Redis键值数
- 用来练手还是不错的,分享大家看一下,还是一些新颖点的!哈哈 就是自定义DataGridView,方便每个功能部分调用!简单!再次重
- using System;using System.Collections.Generic;using System.ComponentMo
- 一、组件型注解:1、@Component 在类定义之前添加@Component注解,他会被spring容器识别,并转为bean。2、@Rep
- GoogleNow是Android4.1全新推出的一款应用他,它可以全面了解你的使用习惯,并为你提供现在或者未来可能用到的各种信息,Goog
- 常量是在编译时已知并在程序的生存期内不发生更改的不可变值。常量使用 const 修饰符进行声明。只有 C# 内置类型(System.Obje
- Web基础和HTTP协议┌─────────┐┌─────────┐
- 在程序中对文件进行压缩解压缩是很重要的功能,不仅能减小文件的体积,还能对文件起到保护作用。如果是生成用户可以下载的文件,还可以极大的减少网络
- Collections工具类Java里关于聚合的工具类,包含有各种有关集合操作的静态多态方法,不能实例化(把构造函数私有化)public c
- 第三方类库源码将一网友的XMPP代码从ADT转到AS时,发现其使用了第三方类库,源码放在了lib下,直接在AS中Import project
- 在日常开发中经常遇到控件不能随着父容器大小的改变而且自动改变控件的所在位置和大小。以下是实现的代码 /// <summary>
- StringString类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。这个