
Use an HTTP client when the other service speaks HTTP; use sockets when you own a lower-level protocol or must implement one. In either case, define how messages end, how much data is acceptable and when waiting should stop. These local Java examples make those decisions visible before external networking complicates the picture.
Start with a loopback lab
The download contains HttpDemo.java and SocketDemo.java, with instructions. Both were compiled and run with Java 25. Each opens its own listener on 127.0.0.1 using an operating-system-assigned port, exchanges a small synthetic message and closes its resources before exit. No external service or account is required.
Download both networking examples and README (ZIP)
javac HttpDemo.java SocketDemo.java
java HttpDemo
java SocketDemoStatus: 200
Body: ready
Echo: PINGThe loopback HTTP connection intentionally uses plain HTTP for a contained exercise. Real network traffic carrying credentials or sensitive content should use the service's supported TLS configuration. The security guide covers trust and hostname checks.
Build an HTTP request with explicit policy
import com.sun.net.httpserver.HttpServer;
import java.net.*;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
public class HttpDemo {
public static void main(String[] args) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
server.createContext("/health", exchange -> {
try (exchange) {
byte[] body = "ready\n".getBytes(StandardCharsets.UTF_8);
exchange.getResponseHeaders().set("Content-Type", "text/plain; charset=utf-8");
exchange.sendResponseHeaders(200, body.length);
exchange.getResponseBody().write(body);
}
});
server.start();
try (HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(2))
.followRedirects(HttpClient.Redirect.NEVER).build()) {
URI uri = URI.create("http://127.0.0.1:" + server.getAddress().getPort() + "/health");
HttpRequest request = HttpRequest.newBuilder(uri)
.timeout(Duration.ofSeconds(5)).GET().build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
System.out.println("Status: " + response.statusCode());
System.out.print("Body: " + response.body());
if (response.statusCode()!=200 || !response.body().equals("ready\n"))
throw new AssertionError("Unexpected local response");
} finally {
server.stop(0);
}
}
}The HttpServer is a tiny local fixture. The application-facing part is the client: build it once for related requests, create a request, send it with a body handler and inspect the response status. HttpClient can reuse connections, so creating one client for every request often defeats useful reuse. Its API documentation explains the client lifetime and response-handling choices.
A 404 or 500 is an HTTP response and normally arrives as a response object; decide what that status means for the operation. An I/O exception indicates a different failure path. This example requires status 200 and exactly ready followed by a newline, then asserts those facts.
ofString stores the body in memory, which is appropriate for this trusted tiny fixture. For external responses, enforce an acceptable body size, verify content type and encoding, and choose a bounded subscriber or controlled streaming design. Streaming also needs a policy for body consumption and closure. A successful header response alone does not prove that the entire body has been read.
Distinguish timeouts from a complete deadline
The client sets a two-second connection timeout and the request sets a five-second timeout. A connection timeout matters when establishing a new connection; an already reused connection has different behavior. A request timeout bounds the relevant HTTP operation under its API contract. Treat streaming-body consumption separately when selecting a handler. See connectTimeout and request timeout.
A whole user operation may include DNS, several calls, retries and body processing. Record one overall deadline and pass the remaining allowance into each step. Five retries with a five-second allowance each can exceed the user's intended wait by a wide margin.
Retry only when the operation's semantics make it safe, with a bounded count, backoff and the remaining deadline. A timeout after sending a write does not establish whether the server applied it. Use the service's idempotency mechanism or reconcile the outcome before repeating a consequential change. The example disables automatic redirects so destination changes stay explicit.
Give a socket protocol a frame
TCP delivers an ordered byte stream. A sender's write does not define a message boundary for the receiver. A protocol must supply one: fixed length, a length prefix or a delimiter with an escaping rule. SocketDemo uses exactly four ASCII bytes, PING, and echoes those four bytes back.
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.*;
public class SocketDemo {
public static void main(String[] args) throws Exception {
try (ServerSocket listener = new ServerSocket()) {
listener.bind(new InetSocketAddress("127.0.0.1", 0));
listener.setSoTimeout(3000);
try (ExecutorService worker = Executors.newSingleThreadExecutor()) {
Future<?> served = worker.submit(() -> {
try (Socket peer = listener.accept()) {
peer.setSoTimeout(3000);
byte[] bytes = peer.getInputStream().readNBytes(4);
if (bytes.length != 4) throw new IllegalStateException("Short message");
peer.getOutputStream().write(bytes);
peer.getOutputStream().flush();
} catch (Exception e) { throw new RuntimeException(e); }
});
try (Socket client = new Socket()) {
client.connect(new InetSocketAddress("127.0.0.1", listener.getLocalPort()), 2000);
client.setSoTimeout(3000);
client.getOutputStream().write("PING".getBytes(StandardCharsets.US_ASCII));
client.getOutputStream().flush();
String reply = new String(client.getInputStream().readNBytes(4), StandardCharsets.US_ASCII);
if (!reply.equals("PING")) throw new AssertionError("Bad echo");
System.out.println("Echo: " + reply);
}
served.get(5, TimeUnit.SECONDS);
}
}
}
}readNBytes(4) keeps reading toward four bytes or end-of-stream, so the code checks the resulting length or value. The client has a two-second connect timeout; the listener and accepted socket use three-second accept/read timeouts. SO_TIMEOUT bounds an individual blocking read, not an entire multi-read protocol exchange or a socket write. A slow peer making periodic progress can still stretch a larger operation. The Socket timeout documentation defines that distinction.
For a real length-prefixed protocol, validate the advertised size against a maximum before allocating a buffer. Define the byte order, character encoding, error messages and close behavior. A convenience readLine() call still needs a message-size policy when peers are untrusted.
Investigate the layer that failed
On a narrow screen, scroll the table horizontally. Keyboard users can focus the table and use the arrow keys.
| Symptom | What it establishes | Next check |
|---|---|---|
| Name resolution failure | The requested name was not resolved on that path | Hostname spelling, resolver configuration and environment-specific DNS. |
| Connection refused | The attempted connection was actively rejected | Host, port, listening service and firewall behavior. |
| Connection timeout | Establishment did not finish in the allowed time | Route, firewall, remote availability and timeout policy. |
| TLS handshake failure | Secure negotiation or validation failed | Certificate chain, hostname, clock and protocol compatibility. |
| HTTP 401, 403, 404 or 500 | An HTTP responder returned that status | Endpoint, identity, authorization, routing and server logs. |
| Read timeout or short socket message | The expected bytes did not arrive under the protocol policy | Peer framing, flush/close behavior and the operation deadline. |
Record request identifiers, timings and exception types, while redacting credentials and sensitive query strings. Check from the same host or container as the application: a successful browser request on a laptop can use a different resolver, proxy and trust store. Compare the controlled loopback success with the failing external step to narrow the investigation.