void waitForOtherPlayers() throws IOException { Global.log("Waiting for other players to register..."); int n = Integer.parseInt(mainServer.readLine()); playerNames = new String[n]; for (int i = 0; i < n; ++i) { String info = mainServer.readLine(); playerNames[i] = info; } // ping back to server for an OK mainServer.println(); mainServer.flush(); Global.log("List of Players:"); for (int i = 0; i < n; ++i) Global.log("\t" + (i + 1) + ": " + playerNames[i]); // get player ID for (int i = 0; i < n; ++i) { if (name.equals(playerNames[i])) { playerID = i; break; } } Global.log("Player ID: " + (playerID + 1)); }
@Override public void cancel(@Nullable String message, @Nullable Throwable cause) { if (cancelCalled) { return; } cancelCalled = true; try { // Cancel is called in exception handling cases, so it may be the case that the // stream was never successfully created. if (stream != null) { Status status = Status.CANCELLED; if (message != null) { status = status.withDescription(message); } if (cause != null) { status = status.withCause(cause); } if (message == null && cause == null) { // TODO(zhangkun83): log a warning with this exception once cancel() has been deleted from // ClientCall. status = status.withCause( new CancellationException("Client called cancel() without any detail")); } stream.cancel(status); } } finally { if (context != null) { context.removeListener(ClientCallImpl.this); } } }
@Override public void halfClose() { Preconditions.checkState(stream != null, "Not started"); Preconditions.checkState(!cancelCalled, "call was cancelled"); Preconditions.checkState(!halfCloseCalled, "call already half-closed"); halfCloseCalled = true; stream.halfClose(); }
@Override public void sendMessage(ReqT message) { Preconditions.checkState(stream != null, "Not started"); Preconditions.checkState(!cancelCalled, "call was cancelled"); Preconditions.checkState(!halfCloseCalled, "call was half-closed"); try { // TODO(notcarl): Find out if messageIs needs to be closed. InputStream messageIs = method.streamRequest(message); stream.writeMessage(messageIs); } catch (Throwable e) { stream.cancel(Status.CANCELLED.withCause(e).withDescription("Failed to stream message")); return; } // For unary requests, we don't flush since we know that halfClose should be coming soon. This // allows us to piggy-back the END_STREAM=true on the last message frame without opening the // possibility of broken applications forgetting to call halfClose without noticing. if (!unaryRequest) { stream.flush(); } }
@Override public void startTheGame() { gameType = ""; try { // receive ping from server gameType = mainServer.readLine(); } catch (IOException ex) { System.err.println("Could not communicate with server: " + ex); Global.onException(); } super.startTheGame(); for (FixedTimer timer : timers) timer.start(); startMarquee(getGameType(), 1000); startMarquee("3"); startMarquee("2"); startMarquee("1"); // receive ping that the game is starting try { // ping back that client is ready mainServer.println(); mainServer.flush(); // wait for if server is ready mainServer.readLine(); } catch (IOException ex) { System.err.println("Could not communicate with server: " + ex); Global.onException(); } turnOnEndGameReceiver(); startMarquee("START!"); }
void registerName() throws IOException, ClientNameException { // send name of player to main server Global.log("Sending player information [" + name + "]..."); mainServer.out.println(name); mainServer.out.flush(); Global.log("Checking if name already exists..."); // check if registered String s = mainServer.readLine(); if (!s.equals("OK")) { Global.log("Error! Name already exists. ClientNameException thrown."); throw new ClientNameException("Client name [" + name + "] already exists in server"); } Global.log("Successfully registered as [" + name + "]"); }
@Override public boolean isReady() { return stream.isReady(); }
@Override public void setMessageCompression(boolean enabled) { checkState(stream != null, "Not started"); stream.setMessageCompression(enabled); }
@Override public void request(int numMessages) { Preconditions.checkState(stream != null, "Not started"); checkArgument(numMessages >= 0, "Number requested must be non-negative"); stream.request(numMessages); }
@Override public void start(final Listener<RespT> observer, Metadata headers) { checkState(stream == null, "Already started"); checkNotNull(observer, "observer"); checkNotNull(headers, "headers"); // Create the context final Deadline effectiveDeadline = min(callOptions.getDeadline(), parentContext.getDeadline()); if (effectiveDeadline != parentContext.getDeadline()) { context = parentContext.withDeadline(effectiveDeadline, deadlineCancellationExecutor); } else { context = parentContext.withCancellation(); } if (context.isCancelled()) { // Context is already cancelled so no need to create a real stream, just notify the observer // of cancellation via callback on the executor stream = NoopClientStream.INSTANCE; callExecutor.execute( new ContextRunnable(context) { @Override public void runInContext() { observer.onClose(statusFromCancelled(context), new Metadata()); } }); return; } final String compressorName = callOptions.getCompressor(); Compressor compressor = null; if (compressorName != null) { compressor = compressorRegistry.lookupCompressor(compressorName); if (compressor == null) { stream = NoopClientStream.INSTANCE; callExecutor.execute( new ContextRunnable(context) { @Override public void runInContext() { observer.onClose( Status.INTERNAL.withDescription( String.format("Unable to find compressor by name %s", compressorName)), new Metadata()); } }); return; } } else { compressor = Codec.Identity.NONE; } prepareHeaders(headers, callOptions, userAgent, decompressorRegistry, compressor); final boolean deadlineExceeded = effectiveDeadline != null && effectiveDeadline.isExpired(); if (!deadlineExceeded) { updateTimeoutHeaders( effectiveDeadline, callOptions.getDeadline(), parentContext.getDeadline(), headers); ClientTransport transport = clientTransportProvider.get(callOptions); stream = transport.newStream(method, headers); } else { stream = new FailingClientStream(DEADLINE_EXCEEDED); } if (callOptions.getAuthority() != null) { stream.setAuthority(callOptions.getAuthority()); } stream.setCompressor(compressor); stream.start(new ClientStreamListenerImpl(observer)); if (compressor != Codec.Identity.NONE) { stream.setMessageCompression(true); } // Delay any sources of cancellation after start(), because most of the transports are broken if // they receive cancel before start. Issue #1343 has more details // Propagate later Context cancellation to the remote side. context.addListener(this, directExecutor()); if (contextListenerShouldBeRemoved) { // Race detected! ClientStreamListener.closed may have been called before // deadlineCancellationFuture was set, thereby preventing the future from being cancelled. // Go ahead and cancel again, just to be sure it was cancelled. context.removeListener(this); } }
@Override public void cancelled(Context context) { stream.cancel(statusFromCancelled(context)); }
@Test public void shutdownBeforeTransportCreatedWithPendingStream() throws Exception { SocketAddress addr = mock(SocketAddress.class); createTransportSet(addr); // First transport is created immediately ClientTransport pick = transportSet.obtainActiveTransport(); assertEquals(ConnectivityState.CONNECTING, transportSet.getState(false)); verify(mockTransportFactory).newClientTransport(addr, AUTHORITY, USER_AGENT); assertNotNull(pick); // Fail this one MockClientTransportInfo transportInfo = transports.poll(); transportInfo.listener.transportShutdown(Status.UNAVAILABLE); assertEquals(ConnectivityState.TRANSIENT_FAILURE, transportSet.getState(false)); transportInfo.listener.transportTerminated(); // Second transport will wait for back-off pick = transportSet.obtainActiveTransport(); assertTrue(pick instanceof DelayedClientTransport); // Start a stream, which will be pending in the delayed transport ClientStream pendingStream = pick.newStream(method, headers, waitForReadyCallOptions, statsTraceCtx); pendingStream.start(mockStreamListener); // Shut down TransportSet before the transport is created. Further call to // obtainActiveTransport() gets failing transports assertEquals(ConnectivityState.TRANSIENT_FAILURE, transportSet.getState(false)); transportSet.shutdown(); assertEquals(ConnectivityState.SHUTDOWN, transportSet.getState(false)); pick = transportSet.obtainActiveTransport(); assertNotNull(pick); assertTrue(pick instanceof FailingClientTransport); verify(mockTransportFactory).newClientTransport(addr, AUTHORITY, USER_AGENT); // Reconnect will eventually happen, even though TransportSet has been shut down fakeClock.forwardMillis(10); assertEquals(ConnectivityState.SHUTDOWN, transportSet.getState(false)); verify(mockTransportFactory, times(2)).newClientTransport(addr, AUTHORITY, USER_AGENT); // The pending stream will be started on this newly started transport after it's ready. // The transport is shut down by TransportSet right after the stream is created. transportInfo = transports.poll(); verify(transportInfo.transport, times(0)).shutdown(); assertEquals(0, fakeExecutor.numPendingTasks()); transportInfo.listener.transportReady(); assertEquals(ConnectivityState.SHUTDOWN, transportSet.getState(false)); verify(transportInfo.transport, times(0)) .newStream(any(MethodDescriptor.class), any(Metadata.class)); assertEquals(1, fakeExecutor.runDueTasks()); verify(transportInfo.transport) .newStream( same(method), same(headers), same(waitForReadyCallOptions), any(StatsTraceContext.class)); verify(transportInfo.transport).shutdown(); transportInfo.listener.transportShutdown(Status.UNAVAILABLE); assertEquals(ConnectivityState.SHUTDOWN, transportSet.getState(false)); verify(mockTransportSetCallback, never()).onTerminated(any(TransportSet.class)); // Terminating the transport will let TransportSet to be terminated. transportInfo.listener.transportTerminated(); assertEquals(ConnectivityState.SHUTDOWN, transportSet.getState(false)); verify(mockTransportSetCallback).onTerminated(transportSet); // No more transports will be created. fakeClock.forwardMillis(10000); assertEquals(ConnectivityState.SHUTDOWN, transportSet.getState(false)); verifyNoMoreInteractions(mockTransportFactory); assertEquals(0, transports.size()); }
@Test public void verifyFailFastAndNonFailFastBehaviors() { int pendingStreamsCount = 0; int failFastPendingStreamsCount = 0; final SocketAddress addr1 = mock(SocketAddress.class); final SocketAddress addr2 = mock(SocketAddress.class); createTransportSet(addr1, addr2); final DelayedClientTransport delayedTransport = (DelayedClientTransport) transportSet.obtainActiveTransport(); assertEquals(ConnectivityState.CONNECTING, transportSet.getState(false)); assertFalse(delayedTransport.isInBackoffPeriod()); // Create a new fail fast stream. ClientStream ffStream = delayedTransport.newStream(method, headers, failFastCallOptions, statsTraceCtx); ffStream.start(mockStreamListener); // Verify it is queued. assertEquals(++pendingStreamsCount, delayedTransport.getPendingStreamsCount()); failFastPendingStreamsCount++; // Create a new non fail fast stream. delayedTransport.newStream(method, headers, waitForReadyCallOptions, statsTraceCtx); // Verify it is queued. assertEquals(++pendingStreamsCount, delayedTransport.getPendingStreamsCount()); // Let this 1st address fail without success. transports.poll().listener.transportShutdown(Status.UNAVAILABLE); assertEquals(ConnectivityState.CONNECTING, transportSet.getState(false)); assertFalse(delayedTransport.isInBackoffPeriod()); // Verify pending streams still in queue. assertEquals(pendingStreamsCount, delayedTransport.getPendingStreamsCount()); // Create a new fail fast stream. delayedTransport.newStream(method, headers, failFastCallOptions, statsTraceCtx); // Verify it is queued. assertEquals(++pendingStreamsCount, delayedTransport.getPendingStreamsCount()); failFastPendingStreamsCount++; // Create a new non fail fast stream delayedTransport.newStream(method, headers, waitForReadyCallOptions, statsTraceCtx); // Verify it is queued. assertEquals(++pendingStreamsCount, delayedTransport.getPendingStreamsCount()); // Let this 2nd address fail without success. Status failureStatus = Status.UNAVAILABLE.withDescription("some unique failure"); transports.poll().listener.transportShutdown(failureStatus); assertEquals(ConnectivityState.TRANSIENT_FAILURE, transportSet.getState(false)); assertTrue(delayedTransport.isInBackoffPeriod()); // Fail fast pending streams should be cleared assertEquals( pendingStreamsCount - failFastPendingStreamsCount, delayedTransport.getPendingStreamsCount()); pendingStreamsCount -= failFastPendingStreamsCount; failFastPendingStreamsCount = 0; fakeExecutor.runDueTasks(); verify(mockStreamListener).closed(same(failureStatus), any(Metadata.class)); // Create a new fail fast stream. delayedTransport.newStream(method, headers, failFastCallOptions, statsTraceCtx); // Verify it is not queued. assertEquals(pendingStreamsCount, delayedTransport.getPendingStreamsCount()); // Create a new non fail fast stream delayedTransport.newStream(method, headers, waitForReadyCallOptions, statsTraceCtx); // Verify it is queued. assertEquals(++pendingStreamsCount, delayedTransport.getPendingStreamsCount()); fakeClock.forwardMillis(10); // Now back-off is over assertEquals(ConnectivityState.CONNECTING, transportSet.getState(false)); assertFalse(delayedTransport.isInBackoffPeriod()); // Create a new fail fast stream. delayedTransport.newStream(method, headers, failFastCallOptions, statsTraceCtx); // Verify it is queued. assertEquals(++pendingStreamsCount, delayedTransport.getPendingStreamsCount()); failFastPendingStreamsCount++; assertEquals(1, failFastPendingStreamsCount); fakeExecutor.runDueTasks(); // Drain new 'real' stream creation; not important to this test. }