Documente Academic
Documente Profesional
Documente Cultură
Raport
Lucrarea de laborator nr.5
Disciplina: Programarea în Rețea
Tema: Aplicații client-server: Sockets API
Chișinău 2020
Scopul lucrării:
Studierea Sockets API. Operații tip pentru conexiuni prin socket pentru
client. Operații tip pentru conexiuni prin socket pentru server.
Obiectivele lucrării:
Realizarea unui chat cu posibilitatea conectării mai multor clienți simultan
Note teoretice:
Modelul client-server este o structură sau arhitectură aplicație distribuită
care partajează procesarea între furnizorii de servicii numiți servere și elementele
care solicită, numite clienți. Clienții și serverele comunică printr-o rețea de
calculatoarea, de obicei prin Internet, având suporturi hardware diferite, dar pot
rula și pe același sistem fizic. Un server (fizic) rulează unul sau mai multe
programe server, care partajează resursele existente cu clienții. Clientul nu
partajează niciuna dintre resursele proprii, ci apelează la resursele serverului prin
funcții server. Clienții inițializează comunicația cu serverele și așteaptă mesajele
acestora. Prin menținerea legăturii între cei doi, indiferent de pauzele care intervin,
se folosește conceptul de sesiune, care de obicei este limitată în timp.
Socket este o interfață între un program de aplicație și serviciul de transport,
fiind furnizat de o bibliotecă sau de sistemul de operare. Se folosește conceptul de
descriptor, fiecare socket fiind tratat asemănător cu un fișier local. Acest descriptor
este transmis aplicației la crearea socket-ului și apoi est utilizat ca argument în
apelurile următoare.
În general, o conexiune de rețea se realizează dinspre o stație (host) numită
client, către o altă stație, numită server. Pentru ca un client să se poată conecta la
un server, are nevoie de două informații: adresa serverului și portul pe care ascultă
aplicația la care se dorește conectarea. Clasa care realizează acest lucru în Java se
numește java.net.Socket. Orice obiect de tip Socket, după instanțiere, are asociate
două stream-uri:
un InputStream de pe care se pot citi date venite de la celălalt capăt al
conexiunii, accesând metoda Socket.getInputStream();
un OutputStream pe care se pot scrie date pentru a fi trimise la celălalt
capăt al conexiunii, accesând cu metoda Socket.getOutputStream().
Rolul unui socket server este de a accepta conexiuni pe la unul sau mai mulți
clienți. Clasa care implementează acest comportament este java.net.ServerSocket.
Pentru ca un ServerSocket să poată funcționa, are nevoie de o singură informație,
și anume portul pe care să accepte conexiuni. Pentru a efectua o conectare
concretă, clasa conține o metodă numită accept().
Codul sursă:
Configurația serverului:
1. SERVER_HOST=127.0.0.1
2. SERVER_PORT=12056
3. SERVER_TIMEOUT=10000
4. SERVER_MAX_CLIENTS=4
Configurația clientului:
1. SERVER_HOST=127.0.0.1
2. SERVER_PORT=12056
Clientul
1. package client
2.
3. import com.sun.media.jfxmedia.logging.Logger
4. import java.io.*
5. import java.net.Socket
6. import java.util.Properties
7. import java.util.logging.Level
8.
9. object ChatClient {
10.
11. private var connected = true
12. private var serverHost: String? = null
13. private var serverPort: Int? = null
14.
15. private lateinit var socket: Socket
16. private lateinit var inputStream: BufferedReader
17. private lateinit var outputStream: PrintWriter
18.
19. fun startClient() {
20. loadClientConfiguration()
21. connectToServer()
22.
23. println("WELCOME")
24. try {
25. BufferedReader(InputStreamReader(System.`in`)).use { userInput ->
26. while (connected) {
27. try {
28. if (inputStream.ready()) {
29. val response = inputStream.readLine()
30. if (response != "CONNECTION_TERMINATED") {
31. println(response)
32. print("> ")
33. } else {
34. connected = false
35. }
36. }
37.
38. if (userInput.ready()) {
39. outputStream.println(userInput.readLine())
40. val response = inputStream.readLine()
41. print("> ")
42. if (response == null) {
43. println("ERROR: The connection to the server has been i
nterrupted.")
44. break
45. } else if (response != "ACK") {
46. println(response)
47. }
48. }
49. } catch (e: IOException) {
50. println("ERROR: The connection to the server has been interrupt
ed.")
51. }
52.
53. }
54. closeConnectionToServer()
55. println("Connection closed.")
56. System.exit(0)
57. }
58. } catch (ioe: IOException) {
59. Logger.logMsg(Level.WARNING.intValue(), ioe.message)
60. }
61.
62. }
63.
64.
65. private fun loadClientConfiguration() {
66. val config = Properties()
67. try {
68. FileInputStream("client.properties").use { inputStream ->
69. config.load(inputStream)
70. serverHost = config.getProperty("SERVER_HOST")
71. serverPort = Integer.parseInt(config.getProperty("SERVER_PORT"))
72. }
73. } catch (e: Exception) {
74. println("ERROR. Please check client configuration.")
75. System.exit(1)
76. }
77. }
78.
79. private fun connectToServer() {
80. try {
81. socket = Socket(serverHost, serverPort!!)
82. inputStream = BufferedReader(InputStreamReader(socket!!.getInputStream()))
83. outputStream = PrintWriter(socket!!.getOutputStream(), true)
84. } catch (e: IOException) {
85. println("ERROR. Please make sure it is online.")
86. closeConnectionToServer()
87. System.exit(1)
88. }
89. }
90.
91. private fun closeConnectionToServer() {
92. try {
93. socket.let { it.close() }
94. inputStream.let { it.close() }
95. outputStream.let { it.close() }
96. } catch (e: IOException) {
97. println("ERROR. Can not close connection.")
98. }
99.
100. }
101. }
Serverul
1. package server
2.
3. import java.io.FileInputStream
4. import java.net.*
5. import java.util.ArrayList
6. import java.util.Properties
7.
8. object ChatServer {
9.
10. private lateinit var host: InetAddress
11. private var port: Int = 0
12. private var timeout: Int = 0
13. private var maxClients: Int = 0
14. private var serverOnline = false
15.
16. private var serverSocket: ServerSocket? = null
17. private var clients: ArrayList<Client>? = null
18.
19. private val onlineUsers: ArrayList<String>
20. get() {
21. val onlineUsers = ArrayList<String>()
22. for (client in clients!!) {
23. onlineUsers.add(client.getUser()!!.userName)
24. }
25. return onlineUsers
26. }
27.
28.
29. @Throws(Exception::class)
30. fun start() {
31. loadServerConfiguration()
32. serverSocket = ServerSocket(port, timeout, host)
33. serverOnline = true
34. println("SERVER: Server listening on $host:$port")
35.
36. clients = ArrayList()
37. while (serverOnline) {
38. val client = Client(serverSocket!!.accept())
39. if (clients!!.size < maxClients) {
40. clients!!.add(client)
41. Thread(client).start()
42. client.notify("SERVER: Connection to server established. Please log in.
")
43. } else {
44. client.notify("SERVER: The chat server is currently full. Please try ag
ain later.")
45. client.stop()
46. }
47. }
48. }
49.
50. fun processInput(client: Client, input: String) {
51. client.notify("ACK")
52. val args = input.split("\\s".toRegex()).dropLastWhile { it.isEmpty() }.toTypedA
rray()
53. when (args[0]) {
54. "new" -> {
55. if (client.getUser() != null) {
56. client.notify("SERVER: You are already logged in")
57. }
58.
59. val userName = args[1]
60. val userPassword = args[2]
61.
62. if (userName.length > 31 || userPassword.length < 4 || userPassword.len
gth > 8) {
63. client.notify("ERROR: Username must be less than 32 characters and
password must be between 4-8 characters.")
64. }
65.
66. if (User.register(userName, userPassword)) {
67. client.notify("Registration successful. Please login.")
68. } else {
69. client.notify("ERROR: Username is already in use")
70. }
71. }
72. "login" -> {
73. try {
74. if (client.getUser() != null) {
75. client.notify("ERROR: You are already logged in")
76. }
77. val user = login(args[1], args[2])
78. if (user == null) {
79. client.notify("ERROR: Username or password incorrect.")
80. } else {
81. client.setUser(user)
82. client.notify("Welcome, " + user.userName + "! You have been su
ccessfully logged in.")
83. }
84. } catch (e: Exception) {
85. client.notify("ERROR: Invalid input. Please try again.")
86. }
87.
88. }
89. "send" -> {
90. try {
91. if (client.getUser() == null) {
92. client.notify("ERROR: You must be logged in to send messages.")
93. } else {
94. val message = StringBuilder()
95. val senderUserId = client.getUser()!!.userName
96. for (i in 2 until args.size) {
97. message.append(" ")
98. message.append(args[i])
99. }
100.
101. if (args[1] == "all") {
102. ChatServer.broadcastMessage(senderUserId, "[$senderU
serId]:$message")
103. } else {
104. val receiverUserName = args[1]
105. if (!ChatServer.sendMessage(senderUserId, receiverUs
erName, message.toString())) {
106. client.notify("ERROR: $receiverUserName is not o
nline")
107. }
108. }
109. }
110. } catch (e: Exception) {
111. client.notify("ERROR: Invalid input. Please try again.")
112. }
113.
114. }
115. "online" -> if (client.getUser() != null) {
116. val users = ChatServer.onlineUsers
117. val response = StringBuilder("Online users: ")
118. response.append(users.toString())
119. client.notify(response.toString())
120. } else {
121. client.notify("ERROR: You must be logged in to see a list of onl
ine users.")
122. }
123. "logout" -> {
124. if (client.getUser() == null) {
125. client.notify("ERROR: You are not logged in.")
126. }
127. logout(client)
128. client.stop()
129. }
130. else -> client.notify("ERROR: Invalid input. Please try again.")
131. }
132. }
133.
134. fun stop() {
135. serverOnline = false
136. try {
137. for (client in clients!!) {
138. client.stop()
139. }
140. serverSocket!!.close()
141. } catch (e: Exception) {
142. println("SERVER ERROR: Server did not shutdown successfully.")
143. }
144. }
145.
146. @Throws(Exception::class)
147. private fun loadServerConfiguration() {
148. val config = Properties()
149. FileInputStream("server.properties").use { inputStream ->
150. config.load(inputStream)
151. host = InetAddress.getByName(config.getProperty("SERVER_HOST"))
152. port = Integer.parseInt(config.getProperty("SERVER_PORT"))
153. timeout = Integer.parseInt(config.getProperty("SERVER_TIMEOUT"))
154. maxClients = Integer.parseInt(config.getProperty("SERVER_MAX_CLIENTS
"))
155. }
156. }
157.
158. private fun login(userName: String, userPassword: String): User? {
159. for (user in User.getUsers()!!) {
160. if (user.userName == userName && user.userPassword == userPassword)
{
161. broadcastMessage(userName, "$userName joined")
162. return user
163. }
164. }
165. return null
166. }
167.
168. private fun broadcastMessage(sender: String, message: String) {
169. for (client in clients!!) {
170. if (client.getUser() != null && client.getUser()!!.userName != sende
r) {
171. client.notify(message)
172. }
173. }
174. println("> $message")
175. }
176.
177. private fun sendMessage(sender: String, userId: String, message: String): Bo
olean {
178. for (client in clients!!) {
179. if (client.getUser()!!.userName == userId) {
180. client.notify("[$sender]:$message")
181. println("> [$sender] (to $userId): $message")
182. return true
183. }
184. }
185. return false
186. }
187.
188. private fun logout(client: Client) {
189. clients!!.remove(client)
190. val userName = client.getUser()!!.userName
191. broadcastMessage(userName, "$userName left")
192. }
193. }
1. package server
2.
3. import java.io.BufferedReader
4. import java.io.IOException
5. import java.io.InputStreamReader
6. import java.io.PrintWriter
7. import java.net.Socket
8.
9. class Client @Throws(IOException::class) constructor(clientSocket: Socket) : Runnable {
10.
11. private var connected = true
12. private var user: User? = null
13.
14. private val inStream: BufferedReader = BufferedReader(InputStreamReader(clientSocke
t.getInputStream()))
15. private val outStream: PrintWriter = PrintWriter(clientSocket.getOutputStream(), tr
ue)
16.
17. override fun run() {
18. var input: String
19. try {
20. while (connected) {
21. input = inStream.readLine()
22. if (input != null) {
23. ChatServer.processInput(this, input)
24. }
25. }
26. } catch (e: IOException) {
27. outStream.println("SERVER ERROR: Invalid input. Please try again.")
28. }
29.
30. }
31.
32. fun notify(message: String) {
33. outStream.println(message)
34. }
35.
36. fun stop() {
37. connected = false
38. closeConnection()
39. }
40.
41. fun getUser() = user
42.
43. fun setUser(user: User) {
44. this.user = user
45. }
46.
47. private fun closeConnection() {
48. try {
49. with(outStream) {
50. print("CONNECTION_TERMINATED")
51. close()
52. }
53. inStream.close()
54. } catch (e: Exception) {
55. println("SERVER ERROR: Exception $e")
56. }
57. }
58.
59. }
60. package server
61.
62. import java.io.*
63. import java.util.ArrayList
64.
65. class User private constructor(
66. val userName: String,
67. val userPassword: String
68. ) : Serializable {
69.
70. companion object {
71.
72. private var users: ArrayList<User>? = null
73. private val userFile = System.getProperty("user.dir") + "/users.txt"
74.
75. fun register(userId: String, password: String): Boolean {
76. if (users == null) { loadUsers() }
77. val user = User(userId, password)
78. for (existingUser in users!!) {
79. if (existingUser.userName === userId) {
80. return false
81. }
82. }
83. users!!.add(user)
84. return saveUsers()
85. }
86.
87. private fun loadUsers(): Boolean {
88. val fileInputStream: FileInputStream
89. val objectInputStream: ObjectInputStream
90. users = ArrayList()
91.
92. val file = File(userFile)
93. try {
94. file.createNewFile()
95.
96. fileInputStream = FileInputStream(file)
97. objectInputStream = ObjectInputStream(fileInputStream)
98.
99. var user: User
100. while ((objectInputStream.readObject() as User) != null) {
101. user = objectInputStream.readObject() as User
102. users!!.add(user)
103. }
104. objectInputStream.close()
105. fileInputStream.close()
106. } catch (e: Exception) {
107. return false
108. }
109. return true
110. }
111.
112. private fun saveUsers(): Boolean {
113. val file = File(userFile)
114. try {
115. file.createNewFile()
116. val fileOutputStream = FileOutputStream(file, false)
117. val objectOutputStream = ObjectOutputStream(fileOutputStream)
118. for (user in users!!) {
119. objectOutputStream.writeObject(user)
120. }
121. objectOutputStream.close()
122. fileOutputStream.close()
123. } catch (e: Exception) {
124. return false
125. }
126.
127. return true
128. }
129.
130. fun getUsers(): ArrayList<User>? {
131. if (users == null) { loadUsers() }
132. return users
133. }
134.
135. }
136. }
Figura 1. Urmărirea acțiunilor pe server.
Figura 2. Clientul 1
Figura 3. Clientul 2
Concluzii:
Elaborând această lucrare de laborator am făcut cunoștință conceptul de Socket și
tehnicile de programare în rețea utilizând socket-uri. Operațiile pe un socket pot fi
asemănate cu operațiile de IO pe un fișier, socket-ul fiind asociat handle-ului pe
fișier. Scoket-urile pot fi folosite pentru a face două aplicații să comunice între ele.