Comparison of data writing speeds between AF_UNIX and 127.0.0.1 (AF_INET) loopback addresses

Under Linux, there is a situation where local inter-process communication occurs, and one of them is the server and the others are clients.
This can be done by binding the port on the server and sending the client to the corresponding port of 127.0.0.1. However, this will waste a port and easily cause security risks.

Today I found that when creating a socket on the Linux server, you can use AF_UNIX for the protocol family. The values of AF_LOCAL and AF_UNIX are the same.

What are the benefits of AF_UNIX compared to the 127.0.0.1 loopback address? I read the following blog:
Introduction to Unix domain socket

It is said that UNIX domain socket is more efficient for IPC: it does not need to go through the network protocol stack, packaging and unpacking, calculating checksums, maintaining sequence numbers and responses, etc. I take it for granted that AF_UNIX is faster than 127.0.0.1, so I conducted an experiment.

Find a relatively large file, more than 1G in size, and then
1. Use AF_UNIX to write the client and server. The client reads the file and sends it to the AF_UNIX server. Then the server writes the file and see how long it takes to transfer a file using AF_UNIX.
2. Use 127.0.0.1 to write the client and server. The client reads the file and sends it to the 127.0.0.1 server. Then the server writes the file. See how long it takes to transfer a file using 127.0.0.1.

Without further ado, let’s go straight to the code, starting with AF_UNIX:
AF_UNIX server (unixsocketserver.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>
 
#defineMAXLINE 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;
\t
    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( & amp;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 *) & amp;serun, size) < 0) {<!-- -->
        perror("bind error");
        exit(1);
    }
    printf("UNIX domain socket bound\
");
      
    if (listen(listenfd, 20) < 0) {<!-- -->
        perror("listen error");
        exit(1);
    }
    printf("Accepting connections ...\
");
 
    cliun_len = sizeof(cliun);
    if ((connfd = accept(listenfd, (struct sockaddr *) & amp;cliun, & amp;cliun_len)) < 0){<!-- -->
        perror("accept error");
        goto end;
    }
\t
time_t now, endtime;
now = time(NULL);
    while(1)
{<!-- -->
FD_ZERO( & amp;readmask);
FD_ZERO( & amp;exceptmask);
FD_SET(connfd, & amp;readmask);
FD_SET(connfd, & amp;exceptmask);
tv.tv_sec = 3;
tv.tv_usec = 0;
nready = select(maxfd, & amp;readmask, NULL, & amp;exceptmask, & amp;tv);
if(nready < 0)
{<!-- -->
goto end;
}

if(nready == 0)
{<!-- -->
printf("nready == 0\
");
continue;
}
\t\t
if(FD_ISSET(connfd, & amp;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)\
", writebyte, readbyte);
goto end;
}
}
}
\t\t
if(FD_ISSET(connfd, & amp;exceptmask))
{<!-- -->
printf("select, exceptmask\
");
goto end;
}
}
end:
endtime = time(NULL);
printf("costs %d seconds\
", endtime - now);
if(fp != NULL)
{<!-- -->
fclose(fp);
fp = NULL;
}
close(connfd);
    close(listenfd);
    return 0;
}



AF_UNIX client (unixsocketclient.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;
\t
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);
    }
      
    // Generally, the bind function is called explicitly so that the server can distinguish between different clients.
    memset( & amp;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 *) & amp;cliun, len) < 0) {<!-- -->
        perror("bind error");
        exit(1);
    }
 
    memset( & amp;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 *) & amp;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;
}


Then go to 127.0.0.1:
127.0.0.1 server (loopsocketserver.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( & amp;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*) & amp;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*) & amp;clnt_addr, & amp;clnt_addr_size);
time_t now, endtime;
now = time(NULL);
    while(1)
{<!-- -->
FD_ZERO( & amp;readmask);
FD_ZERO( & amp;exceptmask);
FD_SET(clnt_sock, & amp;readmask);
FD_SET(clnt_sock, & amp;exceptmask);
tv.tv_sec = 3;
tv.tv_usec = 0;
nready = select(maxfd, & amp;readmask, NULL, & amp;exceptmask, & amp;tv);
if(nready < 0)
{<!-- -->
goto end;
}

if(nready == 0)
{<!-- -->
printf("nready == 0\
");
continue;
}
\t\t
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)\
", writebyte, readbyte);
goto end;
}
}
}
\t\t
if(FD_ISSET(clnt_sock, & amp;exceptmask))
{<!-- -->
printf("select, exceptmask\
");
goto end;
}
}
end:
endtime = time(NULL);
printf("costs %d seconds\
", endtime - now);
if(fp != NULL)
{<!-- -->
fclose(fp);
fp = NULL;
}
    close(clnt_sock);
    close(serv_sock);
    return 0;
}


127.0.0.1 client (loopsocketclient.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( & amp;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*) & amp;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;
}

My buffer size is divided into 3 situations (the file size is 1.15G). The buffer size of the code snippet in the above picture is 1000 bytes:
Buffer size 100 bytes: AF_UNIX takes 25 seconds to transfer the file, 127.0.0.1 takes 33 seconds to transfer the file
Buffer size 1000 bytes: AF_UNIX takes 8 seconds to transfer the file, 127.0.0.1 takes 7 seconds to transfer the file
Buffer size 10000 bytes: AF_UNIX takes 5 seconds to transfer the file, 127.0.0.1 takes 4 seconds to transfer the file