目录
一、基本介绍
二、C Demo
1、Stream Domain Socket
1.1 Client
1.2 Server
2、Datagram Domain Socket
2.1 Client
2.2 Server
三、Java 应用
Unix域套接字(Unix Domain Socket)是基于 socket 框架上发展出的一种 IPC 机制。它与网络套接字(TCP 或 UDP)不同。网络套接字可以在不同机器上的进程之间进行通信,Domain socket 只可用于 unix 或 linux 系统内部。
Domain Socket 使用文件系统路径作为地址来标识通信的端点。通信双方可以通过在文件系统中创建一个特殊类型的文件来创建一个 Domain Socket。这个文件可以是一个普通文件,也可以是一个符号链接。两个进程可以通过打开这个文件并使用标准的 socket 读写操作来进行通信。
Domain Socket 提供了一种可靠的、高性能的进程间通信方式。它避免了网络套接字中的一些开销(网络协议栈打包、拆包、校验和计算、排序等等),并且可以利用操作系统的内核缓冲区来实现高效的数据传输。因此,在同一台机器上的进程间进行通信时,Unix域套接字通常比网络套接字更快。
IPC 将应用层数据从一个进程拷贝到另一个进程。IPC 机制本质上是可靠的通讯,而网络传输是不可靠,只能通过传输层或应用层来保证可靠性传输。UNIX Domain Socket 也提供面向流和面向数据包两种 API 接口,类似于 TCP 和 UDP,面向消息的 UNIX Domain Socket 也是可靠的,消息既不会丢失也不会顺序错乱。
与使用网络地址的 socket 不同,domain socket 绑定本机文件地址,而且 socket family 为 AF_UNIX(或AF_LOCAL),这区别于 AF_INET。
// domain file address
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* pathname */
};
/*
family(域) : AF_UNIX
type : SOCK_STREAM/ SOCK_DGRAM :
protocol : 0
*/
int socket(int domain, int type, int protocol)
发送一次消息与接收应答
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFFER_SIZE 1024
const char *filename="/tmp/ip.ipc";
int main()
{
struct sockaddr_un uds_file;
char buffer[BUFFER_SIZE] = "hello world";
uds_file.sun_family = AF_UNIX;
strcpy(uds_file.sun_path,filename);
int sock_fd = socket(AF_UNIX,SOCK_STREAM,0);
if(sock_fd < 0){
printf("Request socket failed\n");
return -1;
}
if(connect(sock_fd,(struct sockaddr *)&uds_file,sizeof(uds_file)) < 0){
printf("connect socket failed(%s)\n", strerror(errno));
return -1;
}
send(sock_fd, buffer, BUFFER_SIZE, 0);
// -------------
int ret = recv(sock_fd, buffer, BUFFER_SIZE, 0);
if(ret < 0){
printf("recv failed\n");
}
printf("client receive %s\n", buffer);
close(sock_fd);
return 0;
}
接收客户端消息与应答,且每个客户端只服务一次。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_CONNECT_NUM 2
#define BUFFER_SIZE 1024
const char filename[50] = "/tmp/ip.ipc";
int main()
{
int fd = socket(AF_UNIX,SOCK_STREAM,0);
if(fd < 0){
printf("Request socket failed!\n");
return -1;
}
// domain address
struct sockaddr_un uds_file;
uds_file.sun_family = AF_UNIX;
// 防止该文件被其它进程使用
unlink(filename);
strcpy(uds_file.sun_path, filename);
if(bind(fd, (struct sockaddr *)&uds_file, sizeof(uds_file)) < 0 ){
printf("bind failed(%s)\n", strerror(errno));
return -1;
}
if(listen(fd, MAX_CONNECT_NUM) < 0){
printf("listen failed!\n");
return -1;
}
char buffer[BUFFER_SIZE];
while(1){
bzero(buffer, BUFFER_SIZE);
// 提取就绪socket连接,只服务一次就关闭(测试使用,实际开发中可以启动线程服务每个就绪的socket连接)
int new_fd = accept(fd, NULL, NULL);
if(new_fd < 0){
printf("accept failed\n");
return -1;
}
int ret = recv(new_fd, buffer, BUFFER_SIZE, 0);
if(ret < 0){
printf("recv failed\n");
}
strcpy(buffer, "nice to meet you!");
send(new_fd, buffer, BUFFER_SIZE, 0);
printf("%s\n", buffer);
close(new_fd);
//break;
}
close(fd);
}
Datagram Domain socket 适合单向传输,双向需要另外定义一个 socket。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SIZE 4096
#define COUNT 1000000
const char filename[50] = "/tmp/ip.ipc";
int main(int argc, char **argv)
{
struct sockaddr_un ipc_file;
memset(&ipc_file, 0, sizeof(struct sockaddr));
ipc_file.sun_family = AF_UNIX;
strcpy(ipc_file.sun_path, filename);
// unix domain socket
int uds = socket(AF_UNIX, SOCK_DGRAM, 0);
if (uds < 0)
{
printf("unix domain socket created failed\n");
return -1;
}
printf("[client] : domain file name (%s)\n", filename);
unsigned char buff[SIZE] = "Hello World";
int res = sendto(uds, buff, SIZE, 0, (struct sockaddr *)&ipc_file, sizeof(struct sockaddr_un));
if (res < 0)
{
printf("send failed\n");
}
printf("over\n");
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SIZE 4097
#define COUNT 1000000
const char filename[50] = "/tmp/ip.ipc";
int main()
{
// domain file
struct sockaddr_un ipc_file;
memset(&ipc_file, 0, sizeof(ipc_file));
ipc_file.sun_family = AF_UNIX;
unlink(filename);
strcpy(ipc_file.sun_path, filename);
// unix domain socket
int uds = socket(AF_UNIX, SOCK_DGRAM, 0);
if (uds < 0)
{
printf("unix domain socket created failed\n");
return -1;
}
// uds bind domain file
if (bind(uds, (struct sockaddr *)&ipc_file, sizeof(ipc_file)) < 0)
{
printf("bind failed\n");
return -1;
}
printf("[server] : domain file name (%s)\n", filename);
// buff
char buff[SIZE] = "";
while ( 1 )
{
int size = recvfrom(uds, buff, SIZE, 0, NULL, NULL);
if ( size < 0 )
{
printf("recv failed\n");
break;
}
printf("recv--(%s)\n", buff);
}
printf("over\n");
close(uds);
}
Java 自 1.7 开始支持。下面例子从网上找的,不一定可以运行,仅作参考。
1、Server
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
try {
// 创建服务端Socket,并指定监听的端口
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("Server started, waiting for client connection...");
// 接受客户端连接
Socket clientSocket = serverSocket.accept();
System.out.println("Client connected.");
// 处理客户端请求
// ...
// 关闭连接
clientSocket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2、Client
import java.io.IOException;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
try {
// 创建客户端Socket,并指定服务器的地址和端口
Socket socket = new Socket("localhost", 8888);
// 发送数据到服务端
// ...
// 读取服务端的响应
// ...
// 关闭连接
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
对于开发环境是 1.6 的,所以需要封装 C 动态库,java 层通过 JNI 进行调用。
public class DomainSocket {
// stream domain socket
public native int domain_socket();
public native int bind(int fd, String sock_file);
public native int listen(int fd, int max_num);
public native int accept(int fd);
public native int connect(int fd, String sock_file);
public native int send(int fd, byte[] buffer, int len);
public native int recv(int fd, byte[] buffer, int len);
public native int close(int fd);
public native String last_error();
}