本地套接字
socket API 原本是为多台主机之间网络通信设计的,同时这种网络 socket 也支持单台主机上的进程间通信,(通过 loopback 地址 127.0.0.1)。Unix本地套接字,其实就是一种专门用于本地(也就是单个主机上的)网络通信的一种方法,它所用的 API 跟我们之前用的网络 socket API 是一样的。
实际上,这种通信方式更类似于IPC(进程间通信)的方式,比如无名管道(pipe)、有名管道(mkfifo)。但是,Unix域套接字所提供的控制方式会更多一些,比如说TCP(字节流套接字)提 供等待连接的功能,UDP(数据报套接字)提供帧同步功能,同时也是全双工的(比如使用 socketpair 创建的流管道中的两个描述符都是既可读又可写的)。
服务器端
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<stddef.h>
#include<arpa/inet.h>
#include<sys/un.h>
#include<string.h>
#define SERV_ADDR "serv.socket"
int main(void)
{
int lfd,cfd,len,size,i;
struct sockaddr_un serv_addr,clie_addr;
char buf[4096];
lfd=socket(AF_UNIX,SOCK_STREAM,0); //本地套接字
bzero(&serv_addr,sizeof(serv_addr));
serv_addr.sun_family = AF_UNIX;
strcpy(serv_addr.sun_path,SERV_ADDR); //路径名
printf("offset:%ld---strlen=%ld\n",offsetof(struct sockaddr_un,sun_path),
strlen(serv_addr.sun_path));
len = offsetof(struct sockaddr_un,sun_path) + strlen(serv_addr.sun_path);
/*
* 偏移量加上路径实际长度为serv_addr结构体实际长度
* #define offsetof(type,member) ((int)&((type *)0)->member) 宏函数
* ((type *)0)把结构体首地址设为0,再计算偏移量,最后强转为int型
* */
unlink(SERV_ADDR); //确保bind之前serv_socket不存在,bind会创建该文件
bind(lfd,(struct sockaddr *)&serv_addr,len);
listen(lfd,20);
printf("Accept.......\n");
while(1)
{
len=sizeof(clie_addr);
cfd=accept(lfd,(struct sockaddr *)&clie_addr,(socklen_t *)&len);
len-= offsetof(struct sockaddr_un,sun_path); //得到文件名长度
clie_addr.sun_path[len] = '\0'; //确保打印时,没有乱码出现
printf("client bind filename %s\n",clie_addr.sun_path);
while((size=read(cfd,buf,sizeof(buf)))>0)
{
for(i=0;i<size;i++)
buf[i]=toupper(buf[i]);
write(cfd,buf,size);
}
close(cfd);
}
}
客户端
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<stddef.h>
#define SERV_ADDR "serv.socket"
#define CLIE_ADDR "clie.socket"
int main(void)
{
int cfd,len;
struct sockaddr_un serv_addr,clie_addr;
char buf[4096];
cfd=socket(AF_UNIX,SOCK_STREAM,0);
bzero(&clie_addr,sizeof(clie_addr));
clie_addr.sun_family=AF_UNIX;
strcpy(clie_addr.sun_path,CLIE_ADDR);
len=offsetof(struct sockaddr_un,sun_path)+strlen(clie_addr.sun_path);
//计算客户端结构体实际大小
unlink(CLIE_ADDR);
bind(cfd,(struct sockaddr *)&clie_addr,len); //客户端也需要bind,不能依赖自动绑定
bzero(&serv_addr,sizeof(serv_addr));
serv_addr.sun_family=AF_UNIX;
strcpy(serv_addr.sun_path,SERV_ADDR);
len=offsetof(struct sockaddr_un,sun_path)+ strlen(serv_addr.sun_path); //有效长度
connect(cfd,(struct sockaddr *)&serv_addr,len);
printf("connect filename :%s\n",serv_addr.sun_path);
while(1)
{
fgets(buf,sizeof(buf),stdin);
write(cfd,buf,strlen(buf));
len=read(cfd,buf,sizeof(buf));
write(STDOUT_FILENO,buf,len);
printf("-------------------------------------\n");
}
close(cfd);
}
测试结果
服务器:
客户端