I am new to Flutter and wanted to create a simple app using Flutter in the front and Go in the back over gRPC. I decided to use Unix Sockets (UDS) instead of TCP. Using UDS when both server and client are in Go works fine. However, when trying to create a gRPC client with Flutter over UDS I get the following error.
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: gRPC Err
or (code: 14, codeName: UNAVAILABLE, message: Error connecting: SocketException:
Failed host lookup: 'unix:///tmp/test.socket' (OS Error: Name or service not kn
own, errno = -2), details: null, rawResponse: null, trailers: {})
I tried looking for similar issues. As far as I know, dart supports UDS starting from SDK 2.8. I tried looking for other functions instead of ClientChannel
I even tried using file:///tmp/test.socket
as input string.
Also, when changing both go server and flutter client to use TCP, it works fine.
Flutter code:
import 'package:flutter/material.dart';
import 'proto/src/service.pbgrpc.dart';
import 'package:grpc/grpc.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int result = 0;
Sequence input = Sequence();
SumServiceClient client = SumServiceClient(ClientChannel(
'unix:///tmp/test.socket',
options:
const ChannelOptions(credentials: ChannelCredentials.insecure())));
void _callGrpcService() async {
input.sequence.add(10);
var response = await client.sumSequence(input);
setState(() {
result = response.sum;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(children: <Widget>[
const Text(
'gRPC example',
),
Text(
'$result',
style: Theme.of(context).textTheme.displayMedium,
),
])),
floatingActionButton: FloatingActionButton(
onPressed: _callGrpcService, child: const Icon(Icons.play_arrow)));
}
}
go server:
package main
import (
"context"
"log"
"net"
"os"
"os/signal"
"syscall"
pb "rpc/demo/proto"
"google.golang.org/grpc"
)
type sumServer struct {
pb.SumServiceServer
}
func (s *sumServer) SumSequence(ctx context.Context, req *pb.Sequence) (*pb.Sum, error) {
var sum int32
for _, element := range req.Sequence {
sum += element
}
return &pb.Sum{
Sum: sum,
}, nil
}
const port = ":9000"
func main() {
laddr, err := net.ResolveUnixAddr("unix", "/tmp/test.socket")
listener, err := net.ListenUnix("unix", laddr)
// listener, err := net.Listen("tcp", "localhost"+port)
grpcServer := grpc.NewServer()
signalChannel := make(chan os.Signal, 1)
signal.Notify(signalChannel, os.Interrupt, os.Kill, syscall.SIGTERM)
go func(c chan os.Signal) {
sig := <-c
log.Printf("Caught signal %s, shutting down.", sig)
grpcServer.Stop()
os.Exit(0)
}(signalChannel)
if err != nil {
log.Fatalf("Failed to start the server %v", err)
}
pb.RegisterSumServiceServer(grpcServer, &sumServer{})
log.Printf("Server started at %v", listener.Addr())
if err := grpcServer.Serve(listener); err != nil {
log.Fatalf("Failed to start gRPC server %v", err)
}
}
proto file:
syntax = "proto3";
option go_package = "./proto";
package sum_service;
service SumService {
rpc SumSequence(Sequence) returns (Sum) {}
}
message Sequence {
repeated int32 sequence = 1;
}
message Sum {
int32 sum = 1;
}
As far as I know, dart supports using unix sockets. Is this a problem with Flutter's socket permission? Is there a function other than ClientChannel
for connecting to UDS? Or am I missing a UDS initialization function in flutter?
Just as I suspected. We need to translate the unix socket to an InternetAddress
. Use
final InternetAddress host = InternetAddress('tmp/test.socket', type:InternetAddressType.unix);
and use host
inside ClientChannel
without a port.
Or plug it in directly:
SumServiceClient client = SumServiceClient(ClientChannel(
InternetAddress('/tmp/test.socket', type: InternetAddressType.unix),
options:
const ChannelOptions(credentials: ChannelCredentials.insecure())));