UNIX domain socket跨语言使用

最近接到一任务,需要在Android上实现跨语言接口调用Java->Go->C,直接用C库岂不是最好的(就是要重构C库,老代码无人能维护了)?首席架构师提出了上述的技术方向,但是呢,gRPC不让用,因为需要服务什么的巴拉巴拉。。。好吧,领导让干咱就干,下面我们就用UNIX socket来一试究竟吧:

Go服务:

func main() {
	os.Remove("/home/duan/unixsockdemo.sock")
	unixAddr, err := net.ResolveUnixAddr("unix", "/home/duan/unixsockdemo.sock")
	if err != nil {
		fmt.Println("failed to open socket", err)
		return
	}
	lis, err := net.ListenUnix("unix", unixAddr)
	if err != nil {
		fmt.Println("failed to listen unix socket", err)
		return
	}
	for {
		conn, err := lis.Accept()
		if err != nil {
			fmt.Println("failed to accept unix socket", err)
			return
		}
		fmt.Println("unix socket connected", conn.LocalAddr().String())
		closeCh := make(chan struct{})
		go handler(context.TODO(), conn, closeCh)
		go readLoop(conn, closeCh)
	}
}

var readBufChan = make(chan []byte)

func handler(ctx context.Context, conn net.Conn, closeCh chan struct{}) {
	for {
		select {
		case <-ctx.Done():
			conn.Close()
			return
		case <-closeCh:
			fmt.Println("remote closed", conn.LocalAddr().String())
			conn.Close()
			return
		case readBuf := <-readBufChan:
			sayHelloReq := &api.SayHelloRequest{}
			err := proto.Unmarshal(readBuf, sayHelloReq)
			if err != nil {
				fmt.Println("failed to unmarshal read buffer to SayHelloRequest", err)
				continue
			}
			fmt.Println("receive unix socket", conn.LocalAddr().String(), sayHelloReq.Name)
			sendBuf, err := proto.Marshal(&api.SayHelloResponse{
				Message: "Hello " + sayHelloReq.Name,
			})
			if err != nil {
				fmt.Println("failed to marshal SayHelloResponse to buffer", err)
				continue
			}
			_, err = conn.Write(sendBuf)
			if err != nil {
				fmt.Println("failed to send to socket,maybe closed", err)
				return
			}
		}
	}
}

func readLoop(conn net.Conn, closeCh chan struct{}) {
	readBuf := make([]byte, 1024)
	for {
		readN, err := conn.Read(readBuf)
		if err != nil {
			fmt.Println("failed to read unix socket", err)
			close(closeCh)
			return
		}
		readBufChan <- readBuf[:readN]
	}

对了,为了方便消息传递,用protobuf,proto如下

syntax = "proto3";

option go_package = "server/api";
option java_multiple_files = true;
option java_package = "org.example.unixsocket.protos";
option java_outer_classname = "ChatterProtos";

package example;

service Chatter {
  rpc SayHello(SayHelloRequest) returns (SayHelloResponse) {}
}

message SayHelloRequest {
  string Name = 1;
}

message SayHelloResponse {
  string message = 1;
}

ok,接下来搞一搞Java调用吧,上代码

        File sockFile=new File("/home/duan/unixsockdemo.sock");
        UnixSocketAddress address=new UnixSocketAddress(sockFile);
        try {
            UnixSocketChannel channel=UnixSocketChannel.open(address);
            UnixSocket unixSocket=new UnixSocket(channel);
            OutputStream outputStream =unixSocket.getOutputStream();
            InputStream inputStream=unixSocket.getInputStream();

            long beginT=System.currentTimeMillis();
            SayHelloRequest request=SayHelloRequest.newBuilder().setName("Java").build();
            System.out.println(String.format("marshal spend %dms",System.currentTimeMillis()-beginT));
            request.writeTo(outputStream);
            System.out.println(String.format("send spend %dms",System.currentTimeMillis()-beginT));
            byte[] bytesRead=new byte[1024];
            int nRead=inputStream.read(bytesRead);
            byte[] bytesResp=new byte[nRead];
            System.arraycopy(bytesRead,0,bytesResp,0,nRead);
            System.out.println(String.format("receive spend %dms",System.currentTimeMillis()-beginT));
            SayHelloResponse response=SayHelloResponse.parseFrom(bytesResp);
            System.out.println(String.format("unmarshal spend %dms",System.currentTimeMillis()-beginT));
            long elapsedMillis=System.currentTimeMillis()-beginT;
            System.out.println(String.format("%s total spend %dms",response.getMessage(),elapsedMillis));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

为什么里面有耗时计算?因为我发现耗时有点高,所以就想看看哪一点高了,

marshal spend 20ms
send spend 49ms
receive spend 49ms
unmarshal spend 57ms
Hello Java total spend 57ms

我们用go调用试试耗时情况呢,

func main() {
	conn, err := net.Dial("unix", "/home/duan/unixsockdemo.sock")
	if err != nil {
		fmt.Println("failed to connect to unix socket", err)
		return
	}
	defer conn.Close()

	beginT := time.Now()
	sendBuf, err := proto.Marshal(&api.SayHelloRequest{Name: "Golang"})
	if err != nil {
		fmt.Println("failed to marshal SayHelloRequest to buffer", err)
		return
	}
	_, err = conn.Write(sendBuf)
	if err != nil {
		fmt.Println("failed to send to unix socket", err)
		return
	}
	recvBuf := make([]byte, 1024)
	recvN, err := conn.Read(recvBuf)
	if err != nil {
		fmt.Println("failed to read unix socket", err)
		return
	}
	sayHelloResp := &api.SayHelloResponse{}
	err = proto.Unmarshal(recvBuf[:recvN], sayHelloResp)
	if err != nil {
		fmt.Println("failed to unmarshal buffer to SayHelloResponse", err)
		return
	}
	microSecDuration := time.Since(beginT).Microseconds()
	fmt.Println(sayHelloResp.Message, microSecDuration)
}
Hello Golang 127

0.1ms。。。我们换C试试?

int main()
{
    int sock = 0;
    int ret = 0;
    struct sockaddr_un sock_addr;
    unsigned char send_buf[1024];
    unsigned char recv_buf[1024];
    kms::SayHelloRequest request;
    kms::SayHelloResponse response;

    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock == -1)
    {
        printf("failed to new unix socket,%d", errno);
        return -1;
    }
    sock_addr.sun_family = AF_UNIX;
    strcpy(sock_addr.sun_path, "/home/duan/unixsockdemo.sock");
    ret = connect(sock, (const sockaddr *)&sock_addr, sizeof(struct sockaddr_un));
    if (ret == -1)
    {
        printf("failed to connect to unix socket,%d", errno);
        return -1;
    }
    auto start_time = system_clock::now();
    request.set_name("CPP");
    if (!request.SerializeToArray(send_buf, 1024))
    {
        printf("failed to serialize SayHelloRequest");
        return -3;
    }
    ssize_t size_sended = write(sock, send_buf, request.ByteSizeLong());
    if (size_sended == -1)
    {
        printf("failed to send to unix socket,%d", errno);
        return -4;
    }
    ssize_t size_recved = recv(sock, recv_buf, 1024, 0);
    if (size_recved == -1)
    {
        printf("failed to receive unix socket,%d", errno);
        return -5;
    }
    if (!response.ParseFromArray(recv_buf, size_recved))
    {
        printf("failed to parse received SayHelloResponse");
        return -6;
    }
    printf("receive %s\n", response.message().c_str());
    duration<double> diff = system_clock::now() - start_time;
    printf("total spend %fs\n", diff.count());

    return 0;
}
total spend 0.000177s

和go差不多。。。嗯,就测试到这里吧,Java这个耗时情况,估计方案要玄乎了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值