AF_UNIX和127.0.0.1(AF_INET)回环地址写数据速度对比

在linux下,存在着这样的情况,本地的进程间通信,并且其中一个是服务端,另外的都是客户端。

服务端通过绑定端口,客户端往127.0.0.1的对应端口发送,即可办到,不过这样会浪费一个端口,同时也容易造成安全隐患。

今天发现linux服务端创建socket的时候,协议族用AF_UNIX即可,AF_LOCAL和AF_UNIX的值是一样的。

而AF_UNIX和127.0.0.1回环地址相比,具有哪些好处呢。本人读了下面博客:
Unix domain socket 简介

其中里面讲到UNIX domain socket 用于 IPC 更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等。本人想当然认为AF_UNIX速度比127.0.0.1更快,为此鄙人进行了实验。

找一个比较大的文件,超过1G大小,然后

1。用AF_UNIX写客户端和服务端,由客户端读取文件,发送给AF_UNIX服务端,然后服务端写文件,看看用AF_UNIX传递一个文件需要多久。

  1. 用127.0.0.1写客户端和服务端,由客户端读取文件,发送给127.0.0.1服务端,然后服务端写文件,看看用127.0.0.1传递一个文件需要多久。

废话不多说,直接上代码,先上AF_UNIX的:

AF_UNIX服务端(unixsocketserver.c):

c 复制代码
#include <stdlib.h>  
#include <stdio.h>  
#include <stddef.h>  
#include <sys/socket.h>  
#include <sys/un.h>  
#include <errno.h>  
#include <string.h>  
#include <unistd.h>  
#include <ctype.h>   
 
#define MAXLINE 80  
 
char *socket_path = "/tmp/server.socket";  

#define RECV_LEN 1000
 
int main(void)  
{  
	fd_set readmask, exceptmask;
	struct timeval tv;
	int maxfd = FD_SETSIZE;
	int nready = 0;
	FILE *fp = fopen("/tmp/pull_desktop234_copy.flv", "w");
    if(fp == NULL)
    {
        perror("fopen pull_desktop234_copy.flv failed");
        goto end;
    } 
	char buf[RECV_LEN + 1];
	int readbyte, writebyte;
	
    struct sockaddr_un serun, cliun;  
    socklen_t cliun_len;  
    int listenfd, connfd, size;  
 
    if ((listenfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {  
        perror("socket error");  
        exit(1);  
    }  
 
    memset(&serun, 0, sizeof(serun));  
    serun.sun_family = AF_UNIX;  
    strcpy(serun.sun_path, socket_path);  
    size = offsetof(struct sockaddr_un, sun_path) + strlen(serun.sun_path);  
    unlink(socket_path);  
    if (bind(listenfd, (struct sockaddr *)&serun, size) < 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");  
 
    cliun_len = sizeof(cliun);         
    if ((connfd = accept(listenfd, (struct sockaddr *)&cliun, &cliun_len)) < 0){  
        perror("accept error");  
        goto end;  
    }
	
	time_t now, endtime;
	now = time(NULL);
    while(1)
	{
		FD_ZERO(&readmask);
		FD_ZERO(&exceptmask);
		FD_SET(connfd, &readmask);
		FD_SET(connfd, &exceptmask);
		tv.tv_sec = 3;
		tv.tv_usec = 0;
		nready = select(maxfd, &readmask, NULL, &exceptmask, &tv);
		if(nready < 0)
		{
			goto end;
		}

		if(nready == 0)
		{
			printf("nready == 0\n");
			continue;
		}
		
		if(FD_ISSET(connfd, &readmask))
		{
			readbyte = recv(connfd, buf, RECV_LEN, 0);
			if(readbyte < 0)
			{
				perror("readbyte < 0");
				goto end;
			}
			if(readbyte == 0)
			{
				perror("readbyte == 0");
				goto end;
			}
			if(readbyte > 0)
			{
				buf[readbyte] = 0;
				writebyte = fwrite(buf, 1, readbyte, fp);
				if(writebyte != readbyte)
				{
					printf("writebyte(%d) != readbyte(%d)\n", writebyte, readbyte);
					goto end;
				}
			}
		}
		
		if(FD_ISSET(connfd, &exceptmask))
		{
			printf("select, exceptmask\n");
			goto end;
		}
	}  
end:
	endtime = time(NULL);
	printf("costs %d seconds\n", endtime - now);
	if(fp != NULL)
	{
		fclose(fp);
		fp = NULL;
	}	
	close(connfd);
    close(listenfd);  
    return 0;  
}

AF_UNIX客户端(unixsocketclient.c):

c 复制代码
#include <stdlib.h>  
#include <stdio.h>  
#include <stddef.h>  
#include <sys/socket.h>  
#include <sys/un.h>  
#include <errno.h>  
#include <string.h>  
#include <unistd.h>  


#define SEND_LEN 1000
 
char *client_path = "/tmp/client.socket";  
char *server_path = "/tmp/server.socket";  
 
int main() {  
    struct  sockaddr_un cliun, serun;  
    int len;   
    int sockfd, n;  
	
	FILE *fp = fopen("/tmp/pull_desktop234.flv", "r");
    if(fp == NULL)
	{
		perror("fopen pull_desktop234.flv failed");
		goto end;
	}   
 
    if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){  
        perror("client socket error");  
        exit(1);  
    }  
      
    // 一般显式调用bind函数,以便服务器区分不同客户端  
    memset(&cliun, 0, sizeof(cliun));  
    cliun.sun_family = AF_UNIX;  
    strcpy(cliun.sun_path, client_path);  
    len = offsetof(struct sockaddr_un, sun_path) + strlen(cliun.sun_path);  
    unlink(cliun.sun_path);  
    if (bind(sockfd, (struct sockaddr *)&cliun, len) < 0) {  
        perror("bind error");  
        exit(1);  
    }  
 
    memset(&serun, 0, sizeof(serun));  
    serun.sun_family = AF_UNIX;  
    strcpy(serun.sun_path, server_path);  
    len = offsetof(struct sockaddr_un, sun_path) + strlen(serun.sun_path);  
    if (connect(sockfd, (struct sockaddr *)&serun, len) < 0){  
        perror("connect error");  
        exit(1);  
    }  
 
    int sendbyte = 0;
    int alreadysendbyte = 0;
    char buf[SEND_LEN + 1];
    while((n = fread(buf, 1, SEND_LEN, fp)) > 0)
	{
		sendbyte = send(sockfd, buf, n, 0);
		if(sendbyte == -1)
		{
			perror("send error");
			goto end;
		}
        alreadysendbyte += sendbyte;
        while(alreadysendbyte < n)
        {
			sendbyte = send(sockfd, buf + alreadysendbyte, n - alreadysendbyte, 0);
			if(sendbyte == -1)
            {
				perror("send error");
				goto end;
			}
			alreadysendbyte += sendbyte;
		}
	}
end:
	if(fp != NULL)
	{
		fclose(fp);
		fp = NULL;
	}
	close(sockfd);
    return 0;  
}

接着上127.0.0.1的:

127.0.0.1服务端(loopsocketserver.c):

c 复制代码
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define RECV_LEN 1000

int main(){
	fd_set readmask, exceptmask;
	struct timeval tv;
	int maxfd = FD_SETSIZE;
	int nready = 0;
	FILE *fp = fopen("/tmp/pull_desktop234_copy.flv", "w");
    if(fp == NULL)
    {
        perror("fopen pull_desktop234_copy.flv failed");
        goto end;
    } 
	char buf[RECV_LEN + 1];
	int readbyte, writebyte;
    int serv_sock=socket(AF_INET,SOCK_STREAM,0);
 
    struct sockaddr_in serv_addr;
    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    serv_addr.sin_port=htons(9990);
 
    bind(serv_sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
 
    listen(serv_sock,5);
 
    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_size=sizeof(clnt_addr);
    int clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_addr,&clnt_addr_size);
	time_t now, endtime;
	now = time(NULL);
    while(1)
	{
		FD_ZERO(&readmask);
		FD_ZERO(&exceptmask);
		FD_SET(clnt_sock, &readmask);
		FD_SET(clnt_sock, &exceptmask);
		tv.tv_sec = 3;
		tv.tv_usec = 0;
		nready = select(maxfd, &readmask, NULL, &exceptmask, &tv);
		if(nready < 0)
		{
			goto end;
		}

		if(nready == 0)
		{
			printf("nready == 0\n");
			continue;
		}
		
		if(FD_ISSET(clnt_sock, &readmask))
		{
			readbyte = recv(clnt_sock, buf, RECV_LEN, 0);
			if(readbyte < 0)
			{
				perror("readbyte < 0");
				goto end;
			}
			if(readbyte == 0)
			{
				perror("readbyte == 0");
				goto end;
			}
			if(readbyte > 0)
			{
				buf[readbyte] = 0;
				writebyte = fwrite(buf, 1, readbyte, fp);
				if(writebyte != readbyte)
				{
					printf("writebyte(%d) != readbyte(%d)\n", writebyte, readbyte);
					goto end;
				}
			}
		}
		
		if(FD_ISSET(clnt_sock, &exceptmask))
		{
			printf("select, exceptmask\n");
			goto end;
		}
	}
end: 
	endtime = time(NULL);
	printf("costs %d seconds\n", endtime - now);
	if(fp != NULL)
	{
		fclose(fp);
		fp = NULL;
	}
    close(clnt_sock);
    close(serv_sock);
    return 0;
}

127.0.0.1客户端(loopsocketclient.c):

c 复制代码
#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<string.h>
#include <errno.h>
#include <stdlib.h>

#define SEND_LEN 1000

int main(){

    int sock=socket(AF_INET,SOCK_STREAM,0);
    FILE *fp = fopen("/tmp/pull_desktop234.flv", "r");
    if(fp == NULL)
	{
		perror("fopen pull_desktop234.flv failed");
		goto end;
	}   

	int n = 0;
    struct sockaddr_in serv_addr;
    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
    serv_addr.sin_port=htons(9990);

    if(connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0)
	{
		perror("connect failed");
		goto end;
	}
    int sendbyte = 0;
    int alreadysendbyte = 0;
    char buf[SEND_LEN + 1];
    while((n = fread(buf, 1, SEND_LEN, fp)) > 0)
	{
		sendbyte = send(sock, buf, n, 0);
		if(sendbyte == -1)
		{
			perror("send error");
			goto end;
		}
        alreadysendbyte += sendbyte;
        while(alreadysendbyte < n)
        {
			sendbyte = send(sock, buf + alreadysendbyte, n - alreadysendbyte, 0);
			if(sendbyte == -1)
            {
				perror("send error");
				goto end;
			}
			alreadysendbyte += sendbyte;
		}
	}
end:
	if(fp != NULL)
	{
		fclose(fp);
		fp = NULL;
	}
    close(sock);
    return 0;
}

本人的缓冲区大小分为3种情况(文件大小为1.15G),上图代码片段缓冲区大小为1000字节:

缓冲区大小100字节:AF_UNIX传递完文件费时25秒,127.0.0.1传递完文件费时33秒

缓冲区大小1000字节:AF_UNIX传递完文件费时8秒,127.0.0.1传递完文件费时7秒

缓冲区大小10000字节:AF_UNIX传递完文件费时5秒,127.0.0.1传递完文件费时4秒

相关推荐
天诰3 小时前
SA-K-11 检索会消失吗与四个未解问题
java·服务器·人工智能·python·机器学习·ai编程
陈希1235 小时前
Linux 防火墙配置实战:firewalld、iptables 与云安全组
linux·运维·服务器
女神下凡6 小时前
芯参谋(95):嵌入式文件系统_开发指南
linux·服务器·arm开发·驱动开发·设计规范
@兽血沸腾6 小时前
Windows系统安装Jenkins部署应用至远程Windows服务器操作指南
运维·服务器·jenkins
讯捷蓝达11 小时前
戴尔R720xd服务器卡死在 “Configuring Memory...Done“,换了内存还是卡?问题根本不在内存
运维·服务器
数字新视界14 小时前
动环监控信创适配在智能机房管理中的应用价值与未来展望
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
爱和冰阔落14 小时前
【Linux网络】TCP 服务器如何并发处理多个客户端?从 fork、僵尸进程到双重 fork
linux·运维·服务器·网络
lisanmengmeng15 小时前
alertmanger 标签 rules 打上分组标签
linux·运维·服务器
Android系统攻城狮15 小时前
Linux FFmpeg深度解析之av_write_frame调用流程与实战(三十三)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
上火的金鱼妹15 小时前
k8s生产实际问题排查(网关,数据库读写等)
运维·服务器