写在前面:本篇主要介绍本地套接字的作用,以及本地套接字跟网路套接字在实现方式上哪里不一样,以及用本地套接字来实现服务器和客户端。
正文:
1、本地套接字的作用:本地套接字的CS模型使用来实现进程间通信IPC的,另外实现进程间通信的别的方式还有:pipe、fifo、mmap(内存映射)、信号等。
2、对比网络套接字的CS模型,本地套接字需要注意的地方。
(1)、创建套接字的时候:int socket(int domain, int type , int protocol);
domain:不再是AF_INET,而是 AF_UNIX,或者AF_LOCAL 。
type:SOCK_STREAM 或者 SOCK_DGRAM 都可以
protocol:0
(2)、bind绑定地址结构的时候: int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
这里的addr:不再是 struct sockaddr_in 类型,而是 struct sockaddr_un 类型,来看一下这种类型:
struct sockaddr_un {
__kernel_sa_family_t sun_family;
char sun_path[UNIX_PATH_MAX];
};
这里的sun_family就是AF_UNIX,而 sun_path ;就是socket文件名。
这里要注意:因为 sun_path server和client都要有,所以client端不能再隐式绑定,也就是说client端必须要调用bind函数来显式绑定自己的地址结构。
另外,bind的第三个参数:addrlen,不能再用sizeof(struct sockaddr_un)来计算,而是用如下方式:
int len = offsetof(struct sockaddr_un, sun_path) + strlen(ser_addr.sun_path); //offsetof宏的头文件#include <stddef.h>
(3)、对比一下这几种socket addr数据结构
3、本地套接字Server和Client的实现,同样,代码的注释也尽可能详细了。
domain server实现:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stddef.h>
#include <unistd.h>
#define SER_PATH "ser_socket"
/*struct sockaddr_un类型*/
/*
struct sockaddr_un {
__kernel_sa_family_t sun_family;
char sun_path[UNIX_PATH_MAX];
};
*/
int main(void)
{
int lfd = -1, cfd = -1;
struct sockaddr_un ser_addr, cli_addr;
int ser_addr_len = 0, cli_addr_len = 0;
char *w_buf = "server receive OK";
unsigned char r_buf[1024] = {0};
ssize_t rd_len = 0;
int ret = 0;
//本地的socket 的第一个参数为:AF_UNIX或者AF_LOCAL
//第二个参数:SOCK_STREAM 或者 SOCK_DGRAM 都可以
//第三个参数:默认0 就可以
lfd = socket(AF_UNIX,SOCK_STREAM,0);
if(lfd < 0)
{
printf("socket error\n");
return -1;
}
//绑定地址结构
memset(&ser_addr,0,sizeof(struct sockaddr_un));
ser_addr.sun_family = AF_UNIX;
strcpy(ser_addr.sun_path,SER_PATH);
//计算domain socket的地址结构的长度的方法如下:
ser_addr_len = offsetof(struct sockaddr_un, sun_path) + strlen(ser_addr.sun_path);
printf("ser_addr_len = %d\n",ser_addr_len);
//为了保证bind的成功率,bind前调用一次 unlink
unlink(SER_PATH);
ret = bind(lfd, (const struct sockaddr *)&ser_addr,ser_addr_len);
if(ret < 0)
{
printf("bind error \n");
return -1;
}
listen(lfd, 20);
//服务器等待客户端连接
cfd = accept(lfd, (struct sockaddr *)&cli_addr, &cli_addr_len);
//计算出客户端的 sun_path 长度
cli_addr_len -= offsetof(struct sockaddr_un, sun_path);
cli_addr.sun_path[cli_addr_len] = '\0';
printf("client filename :%s\n",cli_addr.sun_path);
while(1)
{
memset(r_buf,0,1024);
//服务器接收客户端发过来的数据
rd_len = read(cfd, r_buf, 1024);
printf("server receive context:%s\n",r_buf);
//给客户端回复
write(cfd, w_buf, strlen(w_buf));
}
return 0;
}
domain client的实现
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stddef.h>
/*struct sockaddr_un类型*/
/*
struct sockaddr_un {
__kernel_sa_family_t sun_family;
char sun_path[UNIX_PATH_MAX];
};
*/
#define CLI_PATH "cli_socket"
#define SER_PATH "ser.socket"
int main(void)
{
int cfd = -1;
struct sockaddr_un cli_addr , ser_addr;
int cli_addr_len = 0,ser_addr_len = 0;
char *w_buf = "Hello this is client";
int rd_len = 0;
unsigned char r_buf[1024] = {0};
cfd = socket(AF_UNIX,SOCK_STREAM,0);
if(cfd < 0)
{
printf("socket error\n");
return -1;
}
//客户端也要绑定自己的地址结构
//否在无法跟自己对应的文件名建立关系
memset(&cli_addr,0,sizeof(cli_addr));
cli_addr.sun_family = AF_UNIX;
strcpy(cli_addr.sun_path,CLI_PATH);
//地址结构长度计算
cli_addr_len = offsetof(struct sockaddr_un,sun_family) + strlen(cli_addr.sun_path);
unlink(CLI_PATH);
if(bind(cfd, (const struct sockaddr *)&cli_addr,cli_addr_len) < 0)
{
printf("bind error\n");
return -1;
}
//连接服务器
//注意:连接服务器的时候也要传入服务器的地址结构
memset(&ser_addr,0,sizeof(ser_addr));
ser_addr.sun_family = AF_UNIX;
strcpy(ser_addr.sun_path,SER_PATH);
ser_addr_len = offsetof(struct sockaddr_un,sun_family) + strlen(ser_addr.sun_path);
connect(cfd, (const struct sockaddr *)&ser_addr,ser_addr_len);
while(1)
{
write(cfd, w_buf, strlen(w_buf));
memset(r_buf,0,1024);
rd_len = read(cfd, r_buf, 1024);
sleep(4); //每隔4秒通信一次
}
return 0;
}