Skip to content

Commit 42b20ad

Browse files
authored
Merge branch 'main' into GP-55_CreateViewResolverExercise
2 parents 8b3bb9a + 9683916 commit 42b20ad

File tree

35 files changed

+1119
-128
lines changed

35 files changed

+1119
-128
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# <img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png" height=50/>Hello Network Socket
2+
3+
Learn **Sockets** – the essential part of Java networking 💪
4+
5+
### Pre-conditions ❗
6+
7+
* You're supposed to have a basic understanding of a computer networking
8+
9+
### Objectives
10+
11+
* **create a server socket** based on a given port ✅
12+
* **accept clients** on the server side ✅
13+
* **read data sent by clients** using `InputStream`
14+
* **create a client socket** connected to the server based on the host and port ✅
15+
* **send data to the server** using cline socket `OutputStream`
16+
* **experiment** with MessageBoard application that consists of a client and server apps ✅
17+
18+
### Exercise overview 🇺🇦
19+
TDB
20+
21+
---
22+
23+
#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-course/tree/main/0-0-intro#introduction)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>1-0-networking-and-http</artifactId>
7+
<groupId>com.bobocode</groupId>
8+
<version>1.0-SNAPSHOT</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>1-0-0-hello-network-socket</artifactId>
13+
<properties>
14+
<maven.compiler.source>11</maven.compiler.source>
15+
<maven.compiler.target>11</maven.compiler.target>
16+
</properties>
17+
18+
19+
</project>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.bobocode.net.client;
2+
3+
import com.bobocode.util.ExerciseNotCompletedException;
4+
import lombok.SneakyThrows;
5+
6+
import java.io.*;
7+
import java.net.Socket;
8+
9+
/**
10+
* A util class that implements all the logic required for building {@link MessageBoardClient}.
11+
*/
12+
public class ClientUtil {
13+
private ClientUtil() {
14+
}
15+
16+
/**
17+
* Using provided host and port it creates an instance of a new {@link Socket} using two-argument constructor.
18+
* This means that returned socket will be already connected to the server, or will throw an error if the connection
19+
* failed.
20+
*
21+
* @param host server host
22+
* @param port server port
23+
* @return an instance of a connected socket
24+
*/
25+
@SneakyThrows
26+
public static Socket openSocket(String host, int port) {
27+
throw new ExerciseNotCompletedException(); // todo: implement according to javadoc and verify by ClientUtilTest
28+
}
29+
30+
/**
31+
* Creates a simple {@link BufferedReader} that allows to read messages from the console.
32+
*
33+
* @return console-based {@link BufferedReader}
34+
*/
35+
@SneakyThrows
36+
public static BufferedReader openConsoleReader() {
37+
InputStreamReader consoleInputStream = new InputStreamReader(System.in);
38+
return new BufferedReader(consoleInputStream);
39+
}
40+
41+
/**
42+
* Prints a prompt and reads a line using provided reader.
43+
*
44+
* @param reader
45+
* @return the message read by reader
46+
*/
47+
@SneakyThrows
48+
public static String readMessage(BufferedReader reader) {
49+
System.out.print("Enter message (q to quit): ");
50+
return reader.readLine();
51+
}
52+
53+
/**
54+
* This is the most important method of this class. It allows writing a string message to the given socket.
55+
* In order to write to the connected socket, it uses its {@link OutputStream}. But since we need to write text
56+
* messages, it creates a {@link BufferedWriter} based on the {@link OutputStream}. A writer allows to
57+
* write {@link String} messages instead of bytes. In order to force sending data to the remote socket,
58+
* it flushed the buffer of the writer using a corresponding method.
59+
*
60+
* @param message a message that should be sent to the socket
61+
* @param socket a socket instance connected to the server
62+
*/
63+
@SneakyThrows
64+
public static void writeToSocket(String message, Socket socket) {
65+
throw new ExerciseNotCompletedException(); // todo: implement according to javadoc and verify by ClientUtilTest
66+
}
67+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.bobocode.net.client;
2+
3+
import com.bobocode.net.server.MessageBoardServer;
4+
import lombok.SneakyThrows;
5+
6+
import java.io.BufferedReader;
7+
import java.net.Socket;
8+
9+
import static com.bobocode.net.client.ClientUtil.*;
10+
11+
/**
12+
* {@link MessageBoardClient} is a client app that allows connecting to the {@link MessageBoardServer} in order to send
13+
* a message. This app reads a message from the console, connects to the server socket and sends a message writing
14+
* data to the output stream. The message then will be printed on the {@link MessageBoardServer} side. The app will
15+
* ask user to type message infinitely, until the user enters `q`.
16+
* <p>
17+
* PLEASE MAKE SURE THAT {@link MessageBoardServer} is running before using the client app.
18+
* <p>
19+
* If you have another machine, you can try to run {@link MessageBoardServer} on that machine. In order to do that, you
20+
* need to make sure that you can find that machine by IP address, and the port you want to use is exposed properly.
21+
* If so, you will need to specify corresponding HOST and POST values below.
22+
*/
23+
public class MessageBoardClient {
24+
private static final String SERVER_ADDRESS = MessageBoardServer.HOST;
25+
private static final int SERVER_PORT = MessageBoardServer.PORT;
26+
27+
@SneakyThrows
28+
public static void main(String[] args) {
29+
try (BufferedReader reader = openConsoleReader()) {
30+
String message = readMessage(reader);
31+
32+
while (!message.equals("q")) {
33+
try (Socket socket = openSocket(SERVER_ADDRESS, SERVER_PORT)) {
34+
writeToSocket(message, socket);
35+
}
36+
message = readMessage(reader);
37+
}
38+
}
39+
}
40+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.bobocode.net.server;
2+
3+
import java.io.IOException;
4+
import java.net.ServerSocket;
5+
import java.net.Socket;
6+
7+
import static com.bobocode.net.server.ServerUtil.*;
8+
9+
/**
10+
* {@link MessageBoardServer} is a server application that allows clients to connect via socket and print a message.
11+
* It opens a server socket on a given port and waits for a client to connect using a infinite loop. Once client is
12+
* connected this app reads a message from the connected socket input stream and prints it to the console.
13+
* <p>
14+
* It uses only one thread. So if multiple clients will try to connect at the same time, they will be waiting and will
15+
* be processed one by one.
16+
*/
17+
public class MessageBoardServer {
18+
public static final String HOST = ServerUtil.getLocalHost();
19+
public static final int PORT = 8899; // you can use any free port you want
20+
21+
public static void main(String[] args) throws IOException {
22+
try (ServerSocket serverSocket = createServerSocket(PORT)) {
23+
while (true) {
24+
try (Socket clientSocket = acceptClientSocket(serverSocket)) {
25+
String message = readMessageFromSocket(clientSocket);
26+
printMessage(clientSocket, message);
27+
}
28+
}
29+
}
30+
}
31+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package com.bobocode.net.server;
2+
3+
import com.bobocode.util.ExerciseNotCompletedException;
4+
import lombok.SneakyThrows;
5+
6+
import java.io.BufferedReader;
7+
import java.io.InputStream;
8+
import java.io.InputStreamReader;
9+
import java.net.InetAddress;
10+
import java.net.ServerSocket;
11+
import java.net.Socket;
12+
import java.time.LocalDateTime;
13+
import java.time.format.DateTimeFormatter;
14+
15+
/**
16+
* Util class for a {@link MessageBoardServer}. It provides all necessary method for network communication. Using these
17+
* utils you can create server socket, accept connection from client, read String message from client socket and print
18+
* it using special provided format.
19+
*/
20+
public class ServerUtil {
21+
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
22+
23+
private ServerUtil() {
24+
}
25+
26+
/**
27+
* A simple method that returns localhost
28+
*
29+
* @return localhost address
30+
*/
31+
@SneakyThrows
32+
public static String getLocalHost() {
33+
return InetAddress.getLocalHost().getHostAddress();
34+
}
35+
36+
/**
37+
* Creates an instance of a {@link ServerSocket} based on the given port.
38+
*
39+
* @param port
40+
* @return a new bound {@link ServerSocket} instance
41+
*/
42+
@SneakyThrows
43+
public static ServerSocket createServerSocket(int port) {
44+
throw new ExerciseNotCompletedException(); // todo: implement according to javadoc and verify by ServerUtilTest
45+
}
46+
47+
/**
48+
* This method accepts a client on a given serverSocket and returns a accepted socket instance.
49+
*
50+
* @param serverSocket an open server socket
51+
* @return an instance of a accepted client socket
52+
*/
53+
@SneakyThrows
54+
public static Socket acceptClientSocket(ServerSocket serverSocket) {
55+
throw new ExerciseNotCompletedException(); // todo: implement according to javadoc and verify by ServerUtilTest
56+
}
57+
58+
/**
59+
* This is the most important method of this class. It allows reading all messages sent by client form the sockets.
60+
* In order to read from a socket, it uses its {@link InputStream}. But since we want to read {@link String}
61+
* messages instead of bytes, it creates a {@link BufferedReader} based on the socket {@link InputStream}.
62+
* Using the reader, it reads a line and returns a {@link String} message.
63+
*
64+
* @param socket an accepted socket
65+
* @return message that was received from the client
66+
*/
67+
@SneakyThrows
68+
public static String readMessageFromSocket(Socket socket) {
69+
throw new ExerciseNotCompletedException(); // todo: implement according to javadoc and verify by ServerUtilTest
70+
}
71+
72+
/**
73+
* Simple message that allows to print message using a nice and informative format. Apart from the text message
74+
* itself, it also prints the time and client address.
75+
*
76+
* @param socket accepted socket
77+
* @param message a text message to print
78+
*/
79+
public static void printMessage(Socket socket, String message) {
80+
InetAddress clientAddress = socket.getInetAddress();
81+
System.out.print(LocalDateTime.now().format(TIME_FORMATTER) + " ");
82+
System.out.printf("[%s]", clientAddress.getHostAddress());
83+
System.out.println(" -- " + message);
84+
}
85+
86+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package com.bobocode.net.client;
2+
3+
import lombok.SneakyThrows;
4+
import org.junit.jupiter.api.*;
5+
import org.junit.jupiter.params.ParameterizedTest;
6+
import org.junit.jupiter.params.provider.ValueSource;
7+
8+
import java.io.BufferedReader;
9+
import java.io.InputStream;
10+
import java.io.InputStreamReader;
11+
import java.net.InetAddress;
12+
import java.net.InetSocketAddress;
13+
import java.net.ServerSocket;
14+
import java.net.Socket;
15+
import java.util.concurrent.CompletableFuture;
16+
17+
import static com.bobocode.net.client.ClientUtil.openSocket;
18+
import static com.bobocode.net.client.ClientUtil.writeToSocket;
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.junit.jupiter.api.Assertions.assertNotNull;
21+
import static org.junit.jupiter.api.Assertions.assertTrue;
22+
23+
24+
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
25+
class ClientUtilTest {
26+
@BeforeEach
27+
public void init() {
28+
}
29+
30+
@Order(1)
31+
@ParameterizedTest
32+
@ValueSource(ints = {8899, 9988})
33+
@DisplayName("openSocket creates an instance of Socket based on given host and port")
34+
@SneakyThrows
35+
void openSocketCreatesSocketConnectedToServer(int port) {
36+
try (var serverSocket = new ServerSocket(port);
37+
var clientSocket = openSocket(serverSocket.getInetAddress().getHostAddress(), port)) {
38+
39+
assertNotNull(clientSocket);
40+
assertThat(clientSocket.getClass()).isEqualTo(Socket.class);
41+
assertThat(clientSocket.getPort()).isEqualTo(port);
42+
InetAddress remoteSocketAddress = ((InetSocketAddress) clientSocket.getRemoteSocketAddress()).getAddress();
43+
assertThat(remoteSocketAddress).isEqualTo(InetAddress.getLocalHost());
44+
}
45+
}
46+
47+
@Order(2)
48+
@Test
49+
@DisplayName("openSocket returns socket that is bound")
50+
@SneakyThrows
51+
void openSocketReturnsBoundSocket() {
52+
int port = 8899;
53+
try (var serverSocket = new ServerSocket(port);
54+
var clientSocket = openSocket(serverSocket.getInetAddress().getHostAddress(), port)) {
55+
56+
assertTrue(clientSocket.isBound());
57+
}
58+
}
59+
60+
@Order(3)
61+
@Test
62+
@DisplayName("openSocket returns socket that is connected")
63+
@SneakyThrows
64+
void openSocketReturnsNotClosedSocket() {
65+
int port = 8899;
66+
try (var serverSocket = new ServerSocket(port);
67+
var clientSocket = openSocket(serverSocket.getInetAddress().getHostAddress(), port)) {
68+
69+
assertTrue(clientSocket.isConnected());
70+
}
71+
}
72+
73+
@Order(4)
74+
@ParameterizedTest
75+
@ValueSource(strings = {"bye-bye", "see ya"})
76+
@DisplayName("writeToSocket sends given messages to server via socket output stream")
77+
@SneakyThrows
78+
void writeToSocketSendsMessageViaOutputStream(String givenMessage) {
79+
int port = 8899;
80+
try (var serverSocket = new ServerSocket(port)) {
81+
CompletableFuture<String> receivedMessageFuture = CompletableFuture.supplyAsync(() -> readMessage(serverSocket));
82+
try (var clientSocket = new Socket(InetAddress.getLocalHost().getHostAddress(), port)) {
83+
84+
writeToSocket(givenMessage, clientSocket);
85+
86+
}
87+
assertThat(receivedMessageFuture.get()).isEqualTo(givenMessage);
88+
}
89+
}
90+
91+
@SneakyThrows
92+
private String readMessage(ServerSocket serverSocket) {
93+
try (var clientSocket = serverSocket.accept()) {
94+
InputStream inputStream = clientSocket.getInputStream();
95+
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
96+
return reader.readLine();
97+
}
98+
}
99+
}

0 commit comments

Comments
 (0)