Appearance
本地套接字
这是一个可靠的本地通信手段
用于本地通信, 对比网络编程, TCP C/S模型, 需要注意
- 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.
- bind这时候不再使用sockaddr_in, 使用sockaddr_un
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
- 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
- socket 使用的协议不同AF_INET(网络), AF_UNIX(本地)
- bind使用的结构体不同, 分别使用的是struct sockaddr_in和struct sockaddr_un, 初始化的时候使用的协议不同, 一个使用ip和端口, 另一个使用文件名
- 本地套接字使用的时候最好使用unlink把这一个文件删除一下
- 之后使用accept函数的时候传出参数sockaddr不同
- 客户端
- socket函数的第一个参数不同
- 网络使用的时候可以隐式绑定, 使用本地套接字的时候不能这么使用
- 之后connect的时候使用的结构体不同