Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use ServerBootstrap to start then start grpc server with same port don't throw BindException: Address already in use #3824

Closed
SeriousMa opened this issue Dec 4, 2017 · 13 comments

Comments

@SeriousMa
Copy link

What version of gRPC are you using?

1.7.0

What did you expect to see?

throw BindException: Address already in use

image

@carl-mastrangelo
Copy link
Contributor

So you expected to see an error, and there wasn't one? From the code, it looks like that can happen. If the thread you start hasn't started yet, the gRPC server can win the race. Actually I would expect the gRPC to win in this case.

@SeriousMa
Copy link
Author

SeriousMa commented Dec 5, 2017

@carl-mastrangelo
add Thread.sleep(5000) between thread and gRPC server
still no Exception

@ejona86
Copy link
Member

ejona86 commented Dec 13, 2017

I reliably get an exception.

I modified the interop server with:

diff --git a/interop-testing/src/main/java/io/grpc/testing/integration/TestServiceServer.java b/interop-testing/src/main/java/io/grpc/testing/integration/TestServiceServer.java
index 48dd37db..3ec70777 100644
--- a/interop-testing/src/main/java/io/grpc/testing/integration/TestServiceServer.java
+++ b/interop-testing/src/main/java/io/grpc/testing/integration/TestServiceServer.java
@@ -23,6 +23,11 @@ import io.grpc.ServerInterceptors;
 import io.grpc.internal.testing.TestUtils;
 import io.grpc.netty.GrpcSslContexts;
 import io.grpc.netty.NettyServerBuilder;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
 import io.netty.handler.ssl.SslContext;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
@@ -44,6 +49,14 @@ public class TestServiceServer {
           + "*.test.google.fr and our test CA. For the Java test client binary, use:\n"
           + "--server_host_override=foo.test.google.fr --use_test_ca=true\n");
     }
+    new ServerBootstrap()
+        .group(new NioEventLoopGroup(1), new NioEventLoopGroup(1))
+        .channel(NioServerSocketChannel.class)
+        .childHandler(new ChannelInitializer<SocketChannel>() {
+            @Override public void initChannel(SocketChannel ch) {}
+          })
+        .bind("127.0.0.1", 8080)
+        .sync();
 
     Runtime.getRuntime().addShutdownHook(new Thread() {
       @Override
$ cd interop-testing
$ ../gradlew installDist
$ ./build/install/grpc-interop-testing/bin/test-server

Using fake CA for TLS certificate. Test clients should expect host
*.test.google.fr and our test CA. For the Java test client binary, use:
--server_host_override=foo.test.google.fr --use_test_ca=true

Exception in thread "main" java.io.IOException: Failed to bind
	at io.grpc.netty.NettyServer.start(NettyServer.java:206)
	at io.grpc.internal.ServerImpl.start(ServerImpl.java:151)
	at io.grpc.internal.ServerImpl.start(ServerImpl.java:75)
	at io.grpc.testing.integration.TestServiceServer.start(TestServiceServer.java:146)
	at io.grpc.testing.integration.TestServiceServer.main(TestServiceServer.java:72)
Caused by: java.net.BindException: Address already in use
	at sun.nio.ch.Net.bind0(Native Method)
	at sun.nio.ch.Net.bind(Net.java:433)
	at sun.nio.ch.Net.bind(Net.java:425)
	at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223)
	at io.netty.channel.socket.nio.NioServerSocketChannel.doBind(NioServerSocketChannel.java:128)
	at io.netty.channel.AbstractChannel$AbstractUnsafe.bind(AbstractChannel.java:558)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.bind(DefaultChannelPipeline.java:1283)
	at io.netty.channel.AbstractChannelHandlerContext.invokeBind(AbstractChannelHandlerContext.java:501)
	at io.netty.channel.AbstractChannelHandlerContext.bind(AbstractChannelHandlerContext.java:486)
	at io.netty.channel.DefaultChannelPipeline.bind(DefaultChannelPipeline.java:989)
	at io.netty.channel.AbstractChannel.bind(AbstractChannel.java:254)
	at io.netty.bootstrap.AbstractBootstrap$2.run(AbstractBootstrap.java:364)
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:403)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:463)
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
	at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
	at java.lang.Thread.run(Thread.java:745)

@SeriousMa
Copy link
Author

SeriousMa commented Dec 14, 2017

it dosen't make sence...

public static void main(String[] args) throws IOException, InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                ServerBootstrap b = new ServerBootstrap();
                b.group(new NioEventLoopGroup(1), new NioEventLoopGroup(1))
                        .channel(NioServerSocketChannel.class)
                        .option(ChannelOption.SO_BACKLOG, 100)
                        .handler(new LoggingHandler(LogLevel.INFO))
                        .childHandler(new ChannelInitializer() {
                            @Override
                            protected void initChannel(Channel ch) throws Exception {

                            }
                        });
                ChannelFuture f = null;
                try {
                    f = b.bind("127.0.0.1", 8080).sync();
                    f.channel().closeFuture().sync();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();

        Thread.sleep(10000);
        Server server = ServerBuilder.forPort(8080)
                .addService(new GreetingServiceImpl()).build();
        System.out.println("Starting server...");
        server.start();
        System.out.println("Server started!");
        server.awaitTermination();
    }

    public static class GreetingServiceImpl extends GreetingServiceGrpc.GreetingServiceImplBase {
        @Override
        public void greeting(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
            System.out.println(request);

            String greeting = "Hello there, " + request.getName();

            HelloResponse response = HelloResponse.newBuilder().setGreeting(greeting).build();

            responseObserver.onNext(response);
            responseObserver.onCompleted();
        }
    }

@ejona86
Copy link
Member

ejona86 commented Dec 16, 2017

@SeriousMa, does that last code snippet fail with BindException, or start without issue? When I ran it the server.start() threw an exception.

@SeriousMa
Copy link
Author

@ejona86 nothing happend..
No exceptions are thrown

@ejona86
Copy link
Member

ejona86 commented Dec 18, 2017

@SeriousMa, I just re-tested with grpc-java 1.7 with the same behavior. I'm running Linux with Java 1.8.0_121. Could there be something about your environment that is different?

@SeriousMa
Copy link
Author

@ejona86 hi
im running on Mac with Java 1.7

@ejona86
Copy link
Member

ejona86 commented Dec 21, 2017

@carl-mastrangelo, could you run the same test I did in #3824 (comment) on your Mac and see if you see the expected bind failure?

@carl-mastrangelo
Copy link
Contributor

carl-mastrangelo commented Jan 3, 2018

@ejona86 I applied the diff and ran the binary. I got no exception when running.

EDIT:

which makes sense lsof says:

java      28946 notcarl        0t0  TCP 127.0.0.1:8080 (LISTEN)
java      28946 notcarl        0t0  TCP *:8080 (LISTEN)

@ejona86
Copy link
Member

ejona86 commented Jan 8, 2018

@SeriousMa, it seems this is just OS X's behavior being different than Linux. Listen on 0.0.0.0 instead of 127.0.0.1 and it should likely fail on OS X as you expected.

@ejona86 ejona86 closed this as completed Jan 8, 2018
@pnambiarsf
Copy link

Ran into an interesting scenario today. grpc java 1.7, Mac OS but reason to suspect Linux as well.

It seems like a grpc server will silently ignore being unable to bind to the IPV4 address for some port if it is able to bind to the IPV6 address.

This was a problem for us because we had a server port conflict in a container but our grpc service appeared to start, although it was unreachable on the IPV4 address. We would have liked the grpc server to fail to start.

Works:

  1. $ nc -l 15001
  2. NettyServerBuilder.forPort( 15001 ).addService(new TestEchoImpl()).build().start();

Fails on step #2 "nc: Address already in use":

  1. NettyServerBuilder.forPort( 15001 ).addService(new TestEchoImpl()).build().start();
  2. $ nc -l 15001

Fails on step #2 "IOException: Failed to bind"

  1. $ nc -l -6 15001
  2. NettyServerBuilder.forPort( 15001 ).addService(new TestEchoImpl()).build().start();

@ejona86
Copy link
Member

ejona86 commented Apr 24, 2018

@pnambiarsf, on my Linux machine the first one fails (java.io.IOException: Failed to bind). It fails in the same way if using nc -l -6 15001 as well. Why do you suspect Linux? I think this is just how these OSes behave.

@lock lock bot locked as resolved and limited conversation to collaborators Sep 28, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants