Java 17 Recipes
Download 3.2 Mb. Pdf ko'rish
|
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: |
Ma'lumotlar bazasi mualliflik huquqi bilan himoyalangan ©fayllar.org 2024
ma'muriyatiga murojaat qiling
ma'muriyatiga murojaat qiling