在线学习课程,课程咨询答疑和新课信息:QQ交流群:422901085进行课程讨论
转自于:https://blog.csdn.net/learnframework/article/details/119046318
1 专门用于跨进程通信的Unix Socket
做过java相关app开发肯定学过socket通信等一些小程序来实现局域网通信,学会了通过ip地址来进行一种网络通信,但这种通信一般用于跨设备访问场景。而我们如果在同一个设备上,只是进程与进程之间的一种通信,那就完全没有必要使用这种网络通信方式,因为明显感觉不合适,进程间本身处于一个设备上,为啥还要通过ip网络,网卡走一圈再回来。所以针对这个问题就出现了本节课要讲的Unix Domain Socket通信。
UNIX Domain SOCKET 是在Socket架构上发展起来的用于同一台主机的进程间通讯(IPC)。它不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序列号应答等。只是将应用层数据从一个进程拷贝到另一个进程。UNIX Domain SOCKET有SOKCET_DGRAM和SOCKET_STREAM两种模式,类似于UDP和TCP,但是面向消息的UNIX socket也是可靠的,消息既不会丢失也不会顺序错乱。
2 与正常网络socket的使用差异
因为默认已经对我们的网络socket有一定了解,没有了解的可以看我上一个blog,这里我们学习Unix Socket时候就只需要重点掌握一些它们的差异既可以。
最大差异看一下socket方法:
int socket(int protofamily, int type, int protocol);
//protofamily:即协议域,又称为协议族(family)。这里我们Unix Socket通信情况下选择AF_UNIX,协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
type:指定socket类型。常用的socket类型有,SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)。
int bind(int sockfd, const struct sockaddr addr, socklen_t addrlen);
这里的bind方法里面sockaddr 在上一节讲解网络通信时候时候用的是sockaddr_in结构体,传入最主要是端口和ip,这里unix socket对应结构体则是sockaddr_un:
struct sockaddr_un
{
uint8_t sun_len;
sa_family_t sun_family; /* AF_LOCAL */
char sun_path[104]; /* null-terminated pathname */
};
sun_path主要传入一个绝对路径就可以,因为不需要ip进行网络通信。
其他部分基本和网络socket一样
通信流程图:
3 实战Unix Socket通信
客户端代码:
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXLINE 80
char *client_path = "client-socket";
char *server_path = "server-socket";
int main() {
struct sockaddr_un cliun, serun;
int len;
char buf[100];
int sockfd, n;
if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){
perror("client socket error");
exit(1);
}
memset(&serun, 0, sizeof(serun));
serun.sun_family = AF_UNIX;
strncpy(serun.sun_path,server_path ,
sizeof(serun.sun_path) - 1);
if (connect(sockfd, (struct sockaddr *)&serun, sizeof(struct sockaddr_un)) < 0){
perror("connect error");
exit(1);
}
printf("please input send char:");
while(fgets(buf, MAXLINE, stdin) != NULL) {
write(sockfd, buf, strlen(buf));
n = read(sockfd, buf, MAXLINE);
if ( n < 0 ) {
printf("the other side has been closed.\n");
}else {
printf("received from server: %s \n",buf);
}
printf("please input send char:");
}
close(sockfd);
return 0;
}
服务端代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXLINE 80
char *socket_path = "server-socket";
int main()
{
struct sockaddr_un serun, cliun;
socklen_t cliun_len;
int listenfd, connfd, size;
char buf[MAXLINE];
int i, n;
if ((listenfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
perror("socket error");
exit(1);
}
memset(&serun, 0, sizeof(serun));
serun.sun_family = AF_UNIX;
strncpy(serun.sun_path,socket_path ,
sizeof(serun.sun_path) - 1);
unlink(socket_path);//这个相当于把之前的地址要移除,不然上一个server没有结束,移除会报错already in use
if (bind(listenfd, (struct sockaddr *)&serun, sizeof(struct sockaddr_un)) < 0) {
perror("bind error");
exit(1);
}
printf("UNIX domain socket bound\n");
if (listen(listenfd, 20) < 0) {
perror("listen error");
exit(1);
}
printf("Accepting connections ...\n");
while(1) {
cliun_len = sizeof(cliun);
if ((connfd = accept(listenfd, (struct sockaddr *)&cliun, &cliun_len)) < 0){
perror("accept error");
continue;
}
printf("new client connect to server,client sockaddr === %s \n",((struct sockaddr *)&cliun)->sa_data);
while(1) {
memset(buf,0,sizeof(buf));
n = read(connfd, buf, sizeof(buf));
if (n < 0) {
perror("read error");
break;
} else if(n == 0) {
printf("EOF\n");
break;
}
printf("received: %s\n", buf);
for(i = 0; i < n; i++) {
buf[i] = toupper(buf[i]);
}
write(connfd, buf, n);
}
close(connfd);
}
close(listenfd);
return 0;
}
结果截图:
服务先启动,然后客户端后启动,然后输入任何一个字符“hello”会立即受到服务端回复的大小字母“HELLO”