Java 17 Recipes


Download 3.2 Mb.
Pdf ko'rish
bet157/245
Sana02.06.2024
Hajmi3.2 Mb.
#1839910
1   ...   153   154   155   156   157   158   159   160   ...   245
Bog'liq
Java 17 Recipes

 How It Works
At its basic level, sockets require a type, IP address, and port. While sockets literature 
has consumed whole books, the main idea is pretty straightforward. Like the post 
office, socket communication relies on addresses. These addresses deliver data. In this 
example, we picked the loopback (the same computer where the program is running) 
address (127.0.0.1) and chose a random port number (2583).
The advantage of the new NIO.2 is that it is asynchronous. By using asynchronous 
calls, you can scale your application without creating thousands of threads for each 
connection. In our example, we take the asynchronous calls and wait for a connection, 
effectively making it single-threaded for the sake of the example, but don’t let that stop 
you from enhancing this example with more asynchronous calls. (Check the recipes on 
the multithreaded section of this book.)
For a client to connect, it requires a socket channel. The NIO.2 API allows the 
creation of asynchronous socket channels. Once a socket channel is created, it needs 
an address to connect to. The socketChannel.connect() operation does not block; 
instead, it returns a Future object (this is a different from traditional NIO, where calling 
socketChannel.connect() block until a connection is established). The Future object 
allows a Java program to continue what it is doing and simply query the status of the 
submitted task. To take the analogy further, instead of waiting at the front door for your 
mail to arrive, you do other stuff and check periodically to see whether the mail has 
arrived. Future objects have methods like isDone() and isCancelled() that let you 
ChApTer 8 InpuT And OuTpuT


310
know if the task is done or canceled. It also has the get() method, which allows you to 
wait for the task to finish. In our example, we use Future.get () to wait for the client 
connection to be established.
Once the connection is established, use Channels.newOutputStream() to create an 
output stream to send information. Using the decorator pattern, decorate outputStream 
with ObjectOutputStream to finally send objects through the socket.
The server code is a little more elaborate. Server socket connections allow more 
than one connection to occur. Thus they monitor or receive connections instead of 
initiating a connection. For this reason, the server is usually waiting for a connection 
asynchronously.
The server begins by establishing the address it listens to (127.0.0.1:2583) and 
accepting connections. The call to serverSocketChannel.accept() returns another 
Future object that gives you the flexibility to deal with incoming connections. In our 
example, the server connection simply calls future.get(), which blocks (stop the 
program’s execution) until a connection is accepted.
After the server acquires a socket channel, it creates an inputStream by calling 
Channels.newInputStream(socket) and then wrapping that input stream with an 
ObjectInputStream. The server then proceeds to loop and read each object coming 
from the ObjectInputStream. If the object received’s toString() method equals EOF, the 
server stops looping, and the connection is closed.
Note using 
ObjectOutputStream and ObjectInputStream to send and 
receive a lot of objects can lead to memory leaks. ObjectOutputStream keeps 
a copy of the sent object for efficiency. If you were to send the same object again, 
ObjectOutputStream and ObjectInputStream do not send the same object 
again, but instead, send a previously sent Object Id. This behavior or sending the 
Object Id instead of the whole object raises two issues.
The first issue is that objects that are changed in place (mutable) do not reflect 
the change in the receiving client when sent through the wire. The reason is that 
because the object was sent once, ObjectOutputStream believes that the 
object is already transmitted and only sends the Id, negating any changes to the 
object that have happened since it was sent. To avoid this, don’t make changes 
to objects sent down the wire. This rule also applies to subobjects from the 
object graph.
ChApTer 8 InpuT And OuTpuT


311
The second issue is that because ObjectOutputStream maintains a list of 
sent objects and their Object Ids, if you send a lot of objects, the dictionary of 
sent objects to keys grows indefinitely, causing memory starvation on a long- 
running program. To alleviate this issue, you can call ObjectOutputStream.
reset(), which clears the dictionary of sent objects. Alternatively, you can invoke 
ObjectOutputStream.writeUnshared() to not cache the object in the 
ObjectOutputStream dictionary.

Download 3.2 Mb.

Do'stlaringiz bilan baham:
1   ...   153   154   155   156   157   158   159   160   ...   245




Ma'lumotlar bazasi mualliflik huquqi bilan himoyalangan ©fayllar.org 2024
ma'muriyatiga murojaat qiling