参考博客 https://blog.csdn.net/weixin_41010318/article/details/80221283
socketpair简介
socketpair()函数的声明:
#include <sys/types.h>
#include <sys/socket.h>
int socketpair(int d, int type, int protocol, int sv[2])
创建一对无名的、相互连接的套接字
(1)成功返回0,创建好的套接字分别是sv[0]和sv[1]
(2)失败返回-1,错误码保存于errno中
基本用法:
- 这对套接字可以用于全双工通信,每一个套接字既可以读也可以写。
例如,往sv[0]中写,从sv[1]中读;或者从sv[1]中写,从sv[0]中读; - 如果往一个套接字(如sv[0])中写入后,再从该套接字读,会阻塞。
只能在另一个套接字上(sv[1])上读才能成功。 - 读、写操作可以位于同一个进程,也可以位于不同的进程,如父子进程。
如果是父子进程时,一般会功能分离,一个进程用于读,一个进程用于写。
因为文件描述符 sv[0] 和 sv[1] 是进程间共享的,所以读的进程要关闭写描述符
反之,写的进程关闭读描述符。
和管道和命名管道相比,socketpair有以下特点:
- 全双工
- 可用于任意两个进程之间的通信
一、读写操作位于同一进程
#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;
}
/test24# ./main1
Read result: SOCKET PAIR TEST.
二、读写操作位于父子进程
#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;
}
/test24# ./main
Write success, pid: 2883
Read result: SOCKET PAIR TEST., pid: 2884
三、兄弟进程之间的交互
#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.";
pid_t parent_pid;
pid_t chi1_pid;
pid_t chi2_pid;
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;
}
parent_pid = getpid();
printf("Father, my pid is %d\n", parent_pid);
pid = fork();
if(pid < 0) {
printf("Father, Error, fork failed, errno(%d): %s\n", errno, strerror(errno));
return EXIT_FAILURE;
} else if(pid > 0) {
//父进程
printf("Father, create Child1 (pid: %d)\n", pid);
chi1_pid = pid;
} else if(pid == 0) {
//关闭另外一个套接字
close(socket_pair[0]);
printf("Child1, sleep 5s\n");
sleep(5);
printf("Child1, before read from socket_pair[1]\n");
read(socket_pair[1], buf, sizeof(buf));
printf("Child1, read finish: %s\n", buf);
}
if (getpid() == parent_pid) {
pid = fork();
if(pid < 0) {
printf("Father, Error, fork failed, errno(%d): %s\n", errno, strerror(errno));
return EXIT_FAILURE;
} else if(pid > 0) {
//父进程
printf("Father, create Child2 (pid: %d)\n", pid);
chi2_pid = pid;
} else if(pid == 0) {
//关闭另外一个套接字
close(socket_pair[1]);
printf("Child2, before write to socket_pair[0]\n");
int size = write(socket_pair[0], str, strlen(str));
printf("Child2, write finish: %d\n", size);
}
}
for(;;) {
sleep(1);
}
return EXIT_SUCCESS;
}
Father, my pid is 3063
Father, create Child1 (pid: 3064)
Child1, sleep 5s
Child2, before write to socket_pair[0]
Father, create Child2 (pid: 3065)
Child2, write finish: 17
Child1, before read from socket_pair[1]
Child1, read finish: SOCKET PAIR TEST.
四、 路人进程间的交互
socket 通信不就是不相干进程间的交互嘛
附录1
参考:https://blog.csdn.net/qq_21792169/article/details/50160327
SOCK_STREAM是基于TCP的,可靠连接,采用字节流方式,即以字节为单位传输字节序列
SOCK_DGRAM是基于UDP的,用于局域网,如广播,数据包传输
附录2
fork()简介
可以创建一个子进程
父进程返回子进程的pid,子进程返回0
getpid()获取当前进程id,getppid()获取父进程的id
循环创建N个子进程模型,每个子进程标识自己的身份
父子进程相同:
刚fork后,data段,text段,堆,栈,环境变量,全局变量,宿主目录位置,
进程工作目录位置,信号处理方式
父子进程不同:
进程id、返回值、各自的父进程、进程创建时间、闹钟、未决信号集
读时共享、写时复制
父子进程共享:
全局变量、文件描述符、mmap映射区