Sunteți pe pagina 1din 8

MINISTERUL EDUCAŢIEI, CULTURII și CERCETĂRII al REPUBLICII MOLDOVA

UNIVERSITATEA TEHNICĂ a MOLDOVEI


FACULTATEA CALCULATOARE, INFORMATICĂ și MICROELECTRONICĂ
DEPARTAMENTUL INGINERIA SOFTWARE și AUTOMATICĂ

RAPORT la lucrarea de laborator Nr. 4


Disciplina: Programarea in retea
TEMA: Aplicație Client-Server TCP

Elaborat: st. gr. TI-171 Iepuras Daniel


Verficat: conf. univ. Buldumac Oleg

Chișinău – 2020

Sarcina lucrarii:
Să se creeze o aplicație Client-Server TCP utilizând Sockets API

Mersul lucrarii:

Scopul acestei lucrari de laborator este de a crea o aplicatie de tip chat. Aplicatia se
poate rula pe diferite calculatoare conectate la o retea locala. Pot exista mai multi
clienti care se conecteaza la server si pot discuta intre ei. După ce s-a conectat la
server, un utilizator trebuie să-și furnizeze numele său pentru a intra în chat. Serverul
trimite o listă de utilizatori online în prezent la noul utilizator. Fiecare utilizator este
notificat la sosirea unui nou utilizator și la plecarea acestuia. Fiecare mesaj este
prefixat cu numele de utilizator pentru a urmări cine a trimis mesajul. Și în final,
utilizatorul spune „bye” să renunțe la chat. Aplicația constă din două părți: server și
client. Fiecare parte poate rula independent pe computere separate.

Partea server

Serverul este implementat de doua clase ChatServer si UserThread. Clasa ChatServer


pornește serverul, asculta pe un anumit port de . Când un client nou se conectează, o
instanță a UserThread este creată pentru a servi acel client. Deoarece fiecare
conexiune este procesată într-un thread separat, serverul este capabil să gestioneze
mai mulți clienți în același timp.
public class ChatServer {

private int port;


private Set<String> userNames = new HashSet<>();
private Set<UserThread> userThreads = new HashSet<>();

public ChatServer(int port) {


this.port = port;
}

public void execute() {


try (ServerSocket serverSocket = new ServerSocket(port)) {

System.out.println("Chat Server is listening on port " + port);

while (true) {
Socket socket = serverSocket.accept();
System.out.println("New user connected");

UserThread newUser = new UserThread(socket, this);


userThreads.add(newUser);
newUser.start();

} catch (IOException ex) {


System.out.println("Error in the server: " + ex.getMessage());
ex.printStackTrace();
}
}
public static void main(String[] args) {
if (args.length < 1) {
System.out.println("Syntax: java ChatServer <port-number>");
System.exit(0);
}

int port = Integer.parseInt(args[0]);

ChatServer server = new ChatServer(port);


server.execute();
}

//Trimite mesajul de la un user la altii

void broadcast(String message, UserThread excludeUser) {


for (UserThread aUser : userThreads) {
if (aUser != excludeUser) {
aUser.sendMessage(message);
}
}
}

//adauga numele clientului nou conectat

void addUserName(String userName) {


userNames.add(userName);
}

//Cand clientul este deconectat ii sterge numele si thread-ul

void removeUser(String userName, UserThread aUser) {


boolean removed = userNames.remove(userName);
if (removed) {
userThreads.remove(aUser);
System.out.println("The user " + userName + " quitted");
}
}

Set<String> getUserNames() {
return this.userNames;
}

//Returneaza true daca alti clienti s-au conectat

boolean hasUsers() {
return !this.userNames.isEmpty();
}

Clasa ChatServer are doua colectii Set pentru a urmari numele si firele clientilor
conectati. Set este folosit pentru evitarea dublicatelor si pentru ca ordinea elementelor
nu conteaza.

private Set<String> userNames = new HashSet<>();


private Set<UserThread> userThreads = new HashSet<>();

O metoda importanta din clasa ChatServer este broadcast() care transmite un mesaj de
la client tuturor celorlalti clienti.

void broadcast(String message, UserThread excludeUser) {


for (UserThread aUser : userThreads) {
if (aUser != excludeUser) {
aUser.sendMessage(message);
}
}
}

Clasa UserThread este responsabila pentru citirea mesajelor transmise de la client și


difuzarea mesajelor tuturor celorlalți clienți. În primul rând, trimite o listă de
utilizatori online noului utilizator. Apoi citește numele de utilizator și notifică alți
utilizatori despre noul utilizator.
public class UserThread extends Thread{
private Socket socket;
private ChatServer server;
private PrintWriter writer;

public UserThread(Socket socket, ChatServer server) {


this.socket = socket;
this.server = server;
}

public void run() {


try {
InputStream input = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));

OutputStream output = socket.getOutputStream();


writer = new PrintWriter(output, true);

printUsers();

String userName = reader.readLine();


server.addUserName(userName);

String serverMessage = "New user connected: " + userName;


server.broadcast(serverMessage, this);

String clientMessage;

do {
clientMessage = reader.readLine();
serverMessage = "[" + userName + "]: " + clientMessage;
server.broadcast(serverMessage, this);

} while (!clientMessage.equals("bye"));

server.removeUser(userName, this);
socket.close();

serverMessage = userName + " has quitted.";


server.broadcast(serverMessage, this);

} catch (IOException ex) {


System.out.println("Error in UserThread: " + ex.getMessage());
ex.printStackTrace();
}
}

//trimite lista de clienti online noului client conectat

void printUsers() {
if (server.hasUsers()) {
writer.println("Connected users: " + server.getUserNames());
} else {
writer.println("No other users connected");
}
}

//trimite un mesaj clientului


void sendMessage(String message) {
writer.println(message);
}

Partea client

Clientul este implementat de trei clase: ChatClient, ReadThread, WriteThread.


ChatClient începe programul client, se conectează la un server specificat de ip și
un port. Odată ce conexiunea este făcută, creează și pornește două fire ReadThread si
WriteThread.
public class ChatClient {

private String hostname;


private int port;
private String userName;

public ChatClient(String hostname, int port) {


this.hostname = hostname;
this.port = port;
}

public void execute() {


try {
Socket socket = new Socket(hostname, port);

System.out.println("Connected to the chat server");

new ReadThread(socket, this).start();


new WriteThread(socket, this).start();

} catch (UnknownHostException ex) {


System.out.println("Server not found: " + ex.getMessage());
} catch (IOException ex) {
System.out.println("I/O Error: " + ex.getMessage());
}

void setUserName(String userName) {


this.userName = userName;
}

String getUserName() {
return this.userName;
}

public static void main(String[] args) {


if (args.length < 2) return;

String hostname = args[0];


int port = Integer.parseInt(args[1]);

ChatClient client = new ChatClient(hostname, port);


client.execute();
}

}
Clasa ReadThread este responsabila de citirea intrarilor de la server si imprimarea lor
in mod repetat in consola.
public class ReadThread extends Thread {

private BufferedReader reader;


private Socket socket;
private ChatClient client;

public ReadThread(Socket socket, ChatClient client) {


this.socket = socket;
this.client = client;

try {
InputStream input = socket.getInputStream();
reader = new BufferedReader(new InputStreamReader(input));
} catch (IOException ex) {
System.out.println("Error getting input stream: " + ex.getMessage());
ex.printStackTrace();
}
}

public void run() {


while (true) {
try {
String response = reader.readLine();
System.out.println("\n" + response);

//printeaza numele dupa ce se afiseaza mesajul de la server

if (client.getUserName() != null) {
System.out.print("[" + client.getUserName() + "]: ");
}
} catch (IOException ex) {
System.out.println("Error reading from server: " + ex.getMessage());
ex.printStackTrace();
break;
}
}
}

Clasa WriteThread este responsabila de citirea intrarilor de la client si transmiterea


acestora catre server in mod continuu pana cand utilizatorul tapeaza “bye”.
public class WriteThread extends Thread {

private PrintWriter writer;


private Socket socket;
private ChatClient client;

public WriteThread(Socket socket, ChatClient client) {


this.socket = socket;
this.client = client;

try {
OutputStream output = socket.getOutputStream();
writer = new PrintWriter(output, true);
} catch (IOException ex) {
System.out.println("Error getting output stream: " + ex.getMessage());
ex.printStackTrace();
}
}

public void run() {


Console console = System.console();

String userName = console.readLine("\nEnter your name: ");


client.setUserName(userName);
writer.println(userName);

String text;

do {
text = console.readLine("[" + userName + "]: ");
writer.println(text);

} while (!text.equals("bye"));

try {
socket.close();
} catch (IOException ex) {

System.out.println("Error writing to server: " + ex.getMessage());


}
}

Motivele pentru a rula aceste două fire simultan este faptul că operația de citire
blochează întotdeauna firul curent (atât citirea intrării utilizatorului de pe linia de
comandă, cât și intrarea serverului de citire prin rețea). Asta înseamnă că, dacă thread-
ul curent așteaptă intrarea utilizatorului, nu poate citi intrarea de pe server.
Prin urmare, două fire separate sunt utilizate pentru a face clientul să răspundă: poate
afișa mesaje de la alți utilizatori în timp ce citește mesajul utilizatorului curent.

Rezultate:

Partea server – conectarea a doi client si apoi iesirea unuia din chat.

Partea client:
Concluzie:

In aceasta lucrare de laborator am acumulat cunostinte noi in cum lucreaza


programarea socket a unui server local si cum sa conectezi simultan mai multi
utilizatori utilizand thread-urile.