Java Networking Fundamentals: URI, URL and Sockets
Addresses, sockets and streams. Underneath every network library in Java is a socket carrying bytes.
-
Java Basics
- Introduction to Java
- Setting Up Java and Writing Your First Program
- Variables, Data Types and Literals in Java
- Type Casting and Type Conversion in Java
- Operators and Expressions in Java
- Input and Output in Java
- Comments, Keywords and Naming Conventions in Java
- Control Flow in Java: if, else and switch
- Loops in Java: for, while and do-while
- Methods
- Arrays and Strings
-
OOP
- Classes and Objects in Java
- Constructors in Java
- The this Keyword in Java
- The static Keyword in Java
- Encapsulation in Java
- Access Modifiers in Java
- Inheritance in Java
- Method Overriding and super in Java
- Polymorphism in Java
- Abstraction, Abstract Classes and Interfaces in Java
- Composition, Aggregation and Association in Java
- The Object Lifecycle in Java
- Core Java
- Exception Handling
-
Collections
- The Java Collections Framework
- List in Java: ArrayList, LinkedList, Vector and Stack
- Set in Java: HashSet, LinkedHashSet and TreeSet
- Map in Java: HashMap, LinkedHashMap and TreeMap
- How HashMap Works Internally in Java
- Queue and Deque in Java: ArrayDeque and PriorityQueue
- Iterators in Java
- Comparable and Comparator in Java
- Collections Utilities and Choosing the Right Collection
- Generics
- Java 8+
- Stream API
- Date and Time
- File and I/O
-
Multithreading
- Threads in Java: Processes, Runnable and Thread
- Thread Lifecycle in Java
- Synchronization in Java: synchronized and volatile
- Locks and Atomic Classes in Java
- Race Conditions and Deadlocks in Java
- The Executor Framework and Thread Pools in Java
- Future and CompletableFuture in Java
- Concurrent Collections in Java
- The Java Memory Model
- JVM and Memory
- Advanced Java
- Networking
- JDBC
- Testing
The layers you actually touch
Your code HttpClient, a database driver, a message client
|
Sockets Socket, ServerSocket, SocketChannel
|
TCP or UDP the transport, provided by the operating system
|
IP addressing and routingMost application code stays at the top layer. Understanding the socket underneath explains timeouts, connection refusals and why a stream sometimes returns fewer bytes than asked for.
URI and URL
URI uri = URI.create("https://example.com:8443/notes/42?tab=history#top");
System.out.println(uri.getScheme()); // https
System.out.println(uri.getHost()); // example.com
System.out.println(uri.getPort()); // 8443
System.out.println(uri.getPath()); // /notes/42
System.out.println(uri.getQuery()); // tab=history
System.out.println(uri.getFragment()); // top
URI resolved = uri.resolve("/notes/43");
URI relative = URI.create("/notes").resolve("42");URI | URL | |
|---|---|---|
| Purpose | Parsing and manipulating an identifier | Locating and opening a resource |
| Validates | Syntax only | Requires a known protocol handler |
equals | Textual comparison | May perform a DNS lookup, which is slow |
| Recommendation | Use this | Avoid except when a legacy API demands it |
Never put aURLin aSetor use it as a map key. ItsequalsandhashCodemay resolve host names over the network, so a simple lookup can block.URIhas no such behaviour.
Encoding
String term = "java streams & lambdas";
String encoded = URLEncoder.encode(term, StandardCharsets.UTF_8);
URI search = URI.create("https://example.com/search?q=" + encoded);
String decoded = URLDecoder.decode(encoded, StandardCharsets.UTF_8);Always encode values placed into a query string. Concatenating raw user input produces broken URLs at best and an injected parameter at worst.
Addresses
InetAddress address = InetAddress.getByName("example.com");
System.out.println(address.getHostAddress());
System.out.println(InetAddress.getLocalHost());
InetSocketAddress endpoint = new InetSocketAddress("example.com", 443);A TCP client socket
try (Socket socket = new Socket()) {
socket.connect(new InetSocketAddress("example.com", 80), 5000); // connect timeout
socket.setSoTimeout(10_000); // read timeout
var out = new PrintWriter(
new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true);
var in = new BufferedReader(
new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
String crlf = "" + (char) 13 + (char) 10; // HTTP requires CR LF line endings
out.print("GET / HTTP/1.1" + crlf);
out.print("Host: example.com" + crlf);
out.print("Connection: close" + crlf);
out.print(crlf);
out.flush();
String line;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
}A socket is a two way byte pipe with an input stream and an output stream. Everything above it, HTTP included, is a convention about what those bytes mean.
A TCP server
public class EchoServer {
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(9090);
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
System.out.println("Listening on port 9090");
while (true) {
Socket client = server.accept(); // blocks until a connection arrives
executor.submit(() -> handle(client));
}
}
}
private static void handle(Socket client) {
try (client;
var in = new BufferedReader(new InputStreamReader(client.getInputStream()));
var out = new PrintWriter(client.getOutputStream(), true)) {
String line;
while ((line = in.readLine()) != null) {
out.println("echo: " + line);
}
} catch (IOException e) {
System.err.println("Client failed: " + e.getMessage());
}
}
}One virtual thread per connection is now practical, which is exactly the simple blocking model that used to be too expensive.
UDP
try (DatagramSocket socket = new DatagramSocket()) {
byte[] payload = "ping".getBytes(StandardCharsets.UTF_8);
DatagramPacket packet = new DatagramPacket(
payload, payload.length, InetAddress.getByName("example.com"), 9999);
socket.send(packet);
}| TCP | UDP | |
|---|---|---|
| Connection | Established first | None |
| Delivery | Reliable and ordered | Best effort |
| Overhead | Higher | Lower |
| Suits | Web, databases, most things | Metrics, discovery, streaming media |
Timeouts
Socket socket = new Socket();
socket.connect(endpoint, 5000); // fail fast if the host does not answer
socket.setSoTimeout(10_000); // a read that stalls throws SocketTimeoutExceptionA socket with no timeout can block forever. Every network call in a real system needs both a connect timeout and a read timeout, or a single unresponsive server can exhaust your threads.
Common exceptions
| Exception | Usual cause |
|---|---|
UnknownHostException | DNS could not resolve the name |
ConnectException | Nothing is listening, or a firewall refused |
SocketTimeoutException | Connect or read exceeded the timeout |
SocketException: Connection reset | The peer closed abruptly |
BindException | The port is already in use |
SSLHandshakeException | Certificate or protocol mismatch |
Reading is not message oriented
// Wrong: read may return fewer bytes than requested
byte[] buffer = new byte[1024];
in.read(buffer); // how many were actually read?
// Right
int total = 0;
while (total < expected) {
int read = in.read(buffer, total, expected - total);
if (read == -1) {
throw new EOFException("Stream ended early");
}
total += read;
}
byte[] all = in.readAllBytes(); // when the size is unknown and boundedTCP is a byte stream, not a sequence of messages. A protocol must define its own framing, whether by a length prefix, a delimiter, or closing the connection.
NIO channels, briefly
try (SocketChannel channel = SocketChannel.open(new InetSocketAddress("example.com", 80))) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer);
buffer.flip();
}Channels and selectors allow one thread to manage many connections without blocking. That model existed because threads were expensive; with virtual threads, straightforward blocking code is once again a reasonable choice for most servers.
Common mistakes
- Omitting timeouts.
- Not closing sockets, leaking file descriptors.
- Assuming one
readreturns a whole message. - Using
URLin collections and triggering DNS lookups. - Building URLs by concatenating unencoded input.
- Writing HTTP by hand when
HttpClientexists.
Best practices
- Set both connect and read timeouts on every connection.
- Use try with resources for sockets and streams.
- Use
URIrather thanURL, and encode query values. - Buffer socket streams.
- Define explicit framing in any protocol you design.
- Use
HttpClientfor HTTP rather than raw sockets.
Practice
- Parse a URL with a query and fragment and print every component.
- Write an echo server and connect to it with two clients at once.
- Explain why one
readmay return fewer bytes than requested, and fix a loop accordingly. - Set a one second read timeout and observe the exception.
- Why should a
URLnever be used as a map key?
Conclusion
A socket is a byte pipe, and every higher protocol is a convention on top of it. Use URI for addresses, always set timeouts, frame your own protocols explicitly, and prefer the standard clients to hand written ones.