Skip to content

本地套接字

这是一个可靠的本地通信手段

本地套接字(domain)通信_本地通信套接字-CSDN博客

用于本地通信, 对比网络编程, TCP C/S模型, 需要注意

  1. socket函数的参数domain: AF_INET --> AF_UNIX / AF_LOCAL, 使用协议改变, type可以使用SOCK_STREAM / SOCK_DGRAM都可以

在man手册的unix有AF_UNIX的定义

The AF_UNIX (also known as AF_LOCAL) socket family is used to communi‐ cate between processes on the same machine efficiently. Traditionally, UNIX domain sockets can be either unnamed, or bound to a filesystem pathname (marked as being of type socket). Linux also supports an abstract namespace which is independent of the filesystem.

  1. bind这时候不再使用sockaddr_in, 使用sockaddr_un

img

c
struct sockaddr_un {
    __kernel_sa_family_t sun_family;  /* AF_UNIX */
    char sun_path[UNIX_PATH_MAX];  /* pathname */
};

传入的时候这一个结构体的大小是2(使用的协议)+字符串的大小

c
#define offsetof(type, member) ((int)&((type *)0)->MEMBER)
size = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);

可以使用这一个宏求这一个结构体的实际使用的大小(这一个宏返回path在这一个结构体里面的偏移), 也可以直接使用sizeof

  1. UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。bind成功的话会创建一个socket, 因此为了保证这一个成功, 可以在这之前使用unlink
c
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <stddef.h>
#include <sys/types.h> 
#include <string.h>
#include <sys/un.h>
#include <arpa/inet.h>

#define SERV_ADDR			"serv.addr"


int main(void){
	int lfd, cfd, len, size, i;
	struct sockaddr_un servaddr, cliaddr;
	char buf[1024];
	//获取监听使用的套接字
	lfd = socket(AF_UNIX, SOCK_STREAM, 0);
	if(lfd == -1){
		perror("socket error");
		exit(1);
	}
    //初始化以及绑定自己使用的信息
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sun_family = AF_UNIX;
	strcpy(servaddr.sun_path, SERV_ADDR);
	len = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path);
	unlink(SERV_ADDR);
	bind(lfd, (struct sockaddr*)&servaddr, len);
	//设置监听的数量
	listen(lfd, 20);
	while(1){
		len = sizeof(cliaddr);
        //等待连接
		cfd = accept(lfd, (struct sockaddr *)&cliaddr, (socklen_t *)&len);
		if(cfd < 0){
			perror("accept error");
			exit(1);
		}
		len -= offsetof(struct sockaddr_un, sun_path);
		cliaddr.sun_path[len] = '\0';
		printf("client bind filename %s\n", cliaddr.sun_path);
		while((size = read(cfd, buf, sizeof(buf))) > 0){
			printf("in\n");
			for(i = 0;i<size;i++){
				buf[i] = toupper(buf[i]);
			}
			write(cfd, buf, size);
		}
		printf("close\n");
		close(cfd);
	}
	close(lfd);
	return 0;
}
c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stddef.h>

#define SERV_ADDR			"serv.addr"
#define CLIE_ADDR			"clie.addr"
int main(void){
	int cfd, len;
	struct sockaddr_un servaddr, cliaddr;
	char buf[1024];
	//创建套接字
	cfd = socket(AF_UNIX, SOCK_STREAM, 0);
    //初始化以及绑定自己使用的信息
	bzero(&cliaddr, sizeof(cliaddr));
	cliaddr.sun_family = AF_UNIX;
	strcpy(cliaddr.sun_path, CLIE_ADDR);
	unlink(CLIE_ADDR);
	len = offsetof(struct sockaddr_un, sun_path) + strlen(cliaddr.sun_path);
	int ret = bind(cfd, (struct sockaddr *)&cliaddr, len);
	if(ret < 0){
		perror("bind");
		exit(1);
	}
	bzero(&servaddr, sizeof(servaddr));
	//初始化serv使用的信息
	servaddr.sun_family = AF_UNIX;
	strcpy(servaddr.sun_path, SERV_ADDR);
	len = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path);
    //建立连接
	ret = connect(cfd, (struct sockaddr *)&servaddr,len); 
	if(ret < 0){
		perror("connect error");
		exit(1);
	}
	while(fgets(buf, sizeof(buf), stdin)!=NULL){
		write(cfd, buf, strlen(buf));
		len = read(cfd, buf, sizeof(buf));
		if(len > 0)
			write(STDOUT_FILENO, buf, len);
		else
			exit(0);
	}
	close(cfd);

}

客户端连接的时候可以不使用bind, 这个时候连接的时候使用的文件名为空, 不建议使用

在使用本地套接字的时候使用的结构体sockaddr_un需要头文件<sys/un.h>

和网络套接字的对比

  • server
  1. socket 使用的协议不同AF_INET(网络), AF_UNIX(本地)
  2. bind使用的结构体不同, 分别使用的是struct sockaddr_in和struct sockaddr_un, 初始化的时候使用的协议不同, 一个使用ip和端口, 另一个使用文件名
  3. 本地套接字使用的时候最好使用unlink把这一个文件删除一下
  4. 之后使用accept函数的时候传出参数sockaddr不同
  • 客户端
  1. socket函数的第一个参数不同
  2. 网络使用的时候可以隐式绑定, 使用本地套接字的时候不能这么使用
  3. 之后connect的时候使用的结构体不同