linux 本地套接字 路径,linux本地套接字

本地套接字实现的是同一主机的不同进程间的通信,且建立的通信是双向的通信。socket本地通信与网络通信使用的是统一套接口,只是地址结构中的参数不同。

1、socket流程

(1)创建socket

创建套接字需要使用 socket 系统调用,其原型如下:

int socket(int domain, int type, int protocol);

参数:

domain :指定协议族,对于本地套接字来说,其值须被置为 AF_UNIX 枚举值;

type :套接字类型,type 参数可被设置为 SOCK_STREAM(流式套接字)或 SOCK_DGRAM(数据报式套接字)

protocol 参数指定具体协议,protocol 字段应被设置为 0;

返回:生成的套接字描述符

套接字类型:

流式套接字(SOCK_STREAM)是一个有顺序的、可靠的双向字节流,相当于在本地进程之间建立起一条数据通道;

数据报式套接字(SOCK_DGRAM)相当于单纯的发送消息,在进程通信过程中,理论上可能会有信息丢失、复制或者不按先后次序到达的情况,但由于其在本地通信,不通过外界网络,这些情况出现的概率很小。

//创建本地socket

int server_sockfd = socket(PF_UNIX, SOCK_STREAM, 0);

(2)绑定和连接地址

1)绑定地址

通过绑定地址来指定服务器端的地址(struct sockaddr_un),本地socket的地址是本地文件的地址。

bind 绑定定函数:

int bind(int socket, const struct sockaddr *address, size_t address_len);

参数:

socket表示服务器端的套接字描述符

address 表示需要绑定的本地地址,是一个 struct sockaddr_un 类型的变量

address_len 表示该本地地址的字节长度

本地socket绑定,监听该地址的网络事件

struct sockaddr_un server_addr;

server_addr.sun_family = AF_UNIX;

strncpy(server_addr.sun_path, UNIX_DOMAIN,sizeof(server_addr.sun_path)-1);

bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));

2)连接地址

注册需要连接的服务器地址信息到socket

struct sockaddr_un address;

address.sun_family = AF_UNIX;

strncpy(address.sun_path, UNIX_DOMAIN,sizeof(address.sun_path)-1);

connect(sockfd, (struct sockaddr *)&address, sizeof(address));

2、socket命名

本地套接字的通信双方均需要具有本地地址,指定方式是使用 struct sockaddr_un 的变量sun_path。

struct sockaddr_un {

sa_family_t sun_family; /* AF_UNIX ,2字节*/

char sun_path[UNIX_PATH_MAX]; /* 路径名 */

};

本地socket进程通信的关联文件命名方式有两种:

1)具体文件命名

2)抽象命名空间

(1)具体文件命名

服务器绑定socket会根据此命名创建一个同名的socket文件,客户端连接的时候通过读取该socket文件连接到socket服务端。

这种方式的弊端是服务端必须对socket文件的路径具备写权限,客户端必须知道socket文件路径,且必须对该路径有读权限。

server_addr.sun_family = AF_UNIX;

strncpy(server_addr.sun_path,UNIX_DOMAIN,sizeof(server_addr.sun_path)-1);

server_len = sizeof(struct sockaddr_un);

缺点:

这个文件很容易被其他程序不经意中删除,这导致很奇怪的问题,而很难发现

(2)抽象命名空间

这种方式不需要创建socket文件,只需要命名一个全局名字,即可让客户端根据此名字进行连接。

后者的实现过程与前者的差别是,后者在对地址结构成员sun_path数组赋值的时候,必须把第一个字节置0,即sun_path[0] = 0,代码如:

因第二种方式会对首字节置0,我们可以在命名字符串SERVER_NAME前添加一个占位符@,例如:

#define SERVER_NAME @"/tmp/socket_server"

server_addr.sun_family = AF_UNIX;

strcpy(server_addr.sun_path, SERVER_NAME);

server_addr.sun_path[0]=0;

server_len = strlen(SERVER_NAME) + offsetof(struct sockaddr_un, sun_path);

offsetof函数在#include 头文件中定义,表示sun_path在socketaddr_un 结构中的字节偏移。

正常情况下,sun_path指定了需要bind(或者send/connect等)的路径,若使用abstract_namespace,那么sun_path[0]必须为'\0'(零字符)。同时在使用这个地址时,必须在addresslen参数中指定正确的长度,这个长度是sizeof(sun_family) + 1 + strlen(path)。地址长度不包括path后面的'\0',Linux的系统调用也不会去寻找这个零字符。

当接收(accept,recvfrom等)数据时,Linux也不会在sun_path后面附上一个'\0',所以你必须根据返回的长度谨慎地处理字符串。

实质上,Linux在内存中维护了一个虚拟的"文件系统",这个文件在close之后会自动消失,并且文件系统中看不到bind的文件,用netstat -an却有记录。

若使用netstat查询端口情况,通过abstract_namespace的方式创建的unix域socket bind之后,路径前有一个@

~ 05:17:32 $ netstat -an

unix 2 [ ] DGRAM 7260 @/tmp/cli_path

unix 2 [ ] DGRAM 7259 @/tmp/svr_path

缺点:

移植性较差。

3、本地套接字通信示例

服务器:

#include #include #include #include #include #include //本地socket文件

#define UNIX_DOMAIN "/tmp/UNIX.domain"

//如果使用抽象文件方式,可以使用如 @"/tmp/socket_server"

int main()

{

//如果有本地socket文件,删除本地文件

unlink(UNIX_DOMAIN);

//创建本地socket

int server_sockfd = socket(PF_UNIX, SOCK_STREAM, 0);

struct sockaddr_un server_addr;

server_addr.sun_family = AF_UNIX;

strncpy(server_addr.sun_path, UNIX_DOMAIN,sizeof(server_addr.sun_path)-1); //本地socket文件地址

//绑定socket到本地文件

bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));

listen(server_sockfd, 5);

char ch;

int client_sockfd;

struct sockaddr_un client_addr;

socklen_t len = sizeof(client_addr);

char buffer[1024];

while(1)

{

printf("server waiting:\n");

//接收客户端连接

client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &len);

//读取数据

read(client_sockfd, buffer, sizeof(buffer));

printf("get char from client: %c\n", ch);

//发送数据

write(client_sockfd, buffer, sizeof(buffer));

close(client_sockfd);

}

return 0;

}

客户端:

#include #include #include #include #include #include #define UNIX_DOMAIN "/tmp/UNIX.domain"

//或者本文件夹下的文件 "server_socket"

int main()

{

//创建本地socket

int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);

struct sockaddr_un address;

address.sun_family = AF_UNIX;

strncpy(address.sun_path, UNIX_DOMAIN,sizeof(address.sun_path)-1);

//连接本地socket到本地文件(本物理服务器的)

int result = connect(sockfd, (struct sockaddr *)&address, sizeof(address));

if(result == -1)

{

perror("connect failed: ");

exit(1);

}

char buffer[1024];

//输入数据

fgets (buffer, sizeof(buffer), stdin)

//发送数据

write(sockfd, buffer,sizeof(buffer));

memset(buffer,0,sizeof(buffer));

//阻塞读取服务器返回数据

read(sockfd, buffer,sizeof(buffer));

printf("get char from server: %c\n", ch);

close(sockfd);

return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值