Three elements of network programming
-
Protocol: TCP/UDP
-
IP address
-
Port number
TCP
TCP: transmission control protocol. TCP protocol is a connection oriented communication protocol, that is, before transmitting data, establish a logical connection between the sender and the receiver, and then transmit data. It provides reliable error free data transmission between two computers.
TCP protocol features: connection oriented, secure data transmission and low transmission speed
Three handshakes
• for the first handshake, the client sends a connection request to the server and waits for the server to confirm. What are you worried about?
• for the second handshake, the server sends back a response to the client to notify the client that it has received the connection request. What do I worry about you?
• for the third handshake, the client sends a confirmation message to the server again to confirm the connection. The whole interaction process is shown in the figure below. Try again
After three handshakes are completed and the connection is established, the client and server can start data transmission. Because of this connection oriented feature, TCP protocol can ensure the security of data transmission, so it is widely used, such as downloading files, browsing web pages and so on.
Four handshakes of TCP in socket to release connection
Four waves: in TCP protocol, four waves are required to release the connection after sending data.
-
The first wave: the client puts forward to the server to end the connection and let the server make the final preparations. At this time, the client is in a semi closed state, which means that it no longer sends data to the server, but it can still accept data.
-
Second wave: after receiving the request from the client to release the connection, the server will send the last data to the client. And inform the upper application process that it will no longer receive data.
-
The third wave: after the server sends the data, it will send a message to release the connection to the client. Then the client will know that the connection can be officially released after receiving it.
-
The fourth wave: after receiving the last release connection message from the server, the client shall reply to a completely disconnected message. In this way, the connection will not be completely released until the server receives it.
UDP
UDP: user datagram protocol. UDP protocol is a connectionless protocol. When transmitting data, there is no need to establish a connection. No matter whether the other side service is started or not, the data, data source and destination are directly encapsulated in data packets and sent directly. The size of each packet is limited to 64k. It is an unreliable protocol, because there is no connection, so the transmission speed is fast, but it is easy to lose data. In daily applications, such as shipin meeting, QQ chat, etc.
-
UDP features: no connection oriented, insecure data transmission, fast transmission speed
-
For example, the village head found that Zhang San's cattle had been lost
-
UDP protocol: the village head broadcasts at the village radio station that the cattle of Zhang San's family are lost, the information is lost, and the information release speed is fast
IP
- To view the local IP address, enter:
ipconfig
- Check whether the network is connected. Enter:
ping IP address
ping 220.181.57.216
ping www.baidu.com
Summary
-
Protocol: the rules that computers need to follow when communicating in the network. The common protocols are TCP and UDP
-
TCP: connection oriented, secure data transmission, slow transmission speed
-
UDP: for connectionless, data transmission is not safe, and the transmission speed is fast
-
IP address: used to identify computer equipment in the network
- Classification: IPV4 IPV6
- Local ip address: 127.0.0.1 localhost
-
Port number: used to identify the application program in the computer device, 0 – 65535
- The port number specified by the program written by yourself should be more than 1024
- If the port number is occupied by another service or application, the current program will fail to start.
TCP communication program
// client public class Client { public static void main(String[] args) throws Exception { // Client connection server Socket socket = new Socket("127.0.0.1", 6666); System.out.println("I am a client:" + socket.getLocalPort() + " Start successful"); while (true) { // The client sends data to the server OutputStream out = socket.getOutputStream(); Scanner scanner = new Scanner(System.in); String info = scanner.next(); out.write(("client:"+info).getBytes()); out.flush(); // Client accepts server data InputStream in = socket.getInputStream(); byte[] bytes = new byte[8721]; int len = in.read(bytes); String s = new String(bytes, 0, len); System.out.println(s); } // socket.close(); } }
// Server public class Server { public static void main(String[] args) throws Exception { // Server open port ServerSocket serverSocket = new ServerSocket(6666); System.out.println("I am the server:" + serverSocket.getLocalPort() + " Start successful"); // Wait for the client to connect. It will be blocked before connecting Socket accept = serverSocket.accept(); System.out.println("I'm the server. Hello, client:" + accept.getPort() + ",Welcome to connect~"); while (true) { // The server accepts client data InputStream in = accept.getInputStream(); byte[] bytes = new byte[8721]; int len = in.read(bytes); String s = new String(bytes, 0, len); System.out.println(s); // The server sends data to the client OutputStream out = accept.getOutputStream(); Scanner scanner = new Scanner(System.in); String info = scanner.next(); out.write(("Server:"+info).getBytes()); out.flush(); } // accept.close(); // serverSocket.close(); } }
TCP communication file operation
public class Client { public static void main(String[] args) throws Exception{ // client: // 1. Create a Socket object and specify the ip address and port number of the server to be connected Socket socket = new Socket("127.0.0.1",6666); // 2. Create byte input stream object and associate data source file path FileInputStream fis = new FileInputStream("d:\\img\\a.jpg"); // 3. Obtain the output stream through the Socket OutputStream os = socket.getOutputStream(); // 4. Define a byte array to store the read byte data byte[] bys = new byte[8192]; // 4. Define an int variable to store the number of bytes read int len; // 5. Read data circularly while ((len = fis.read(bys)) != -1) { // 6. In the loop, write out the data os.write(bys, 0, len); } // os.write(""); // -When the file is uploaded, if the client cannot read the data from the file, it will stop sending. // However, the server does not know that the client has stopped writing data, so it will always wait to receive the data written by the client. // Solution: call s.shutdownOutput() on the client; Notify the server that sending is over. socket.shutdownOutput(); System.out.println("=======Start receiving data written back by the server======="); //7. Obtain the input stream through the Socket InputStream is = socket.getInputStream(); //8. Read the string data written back by the server int read = is.read(bys);// Stuck System.out.println(new String(bys,0,read)); // 9. Close the flow and release resources fis.close(); socket.close(); }
public class Server { public static void main(String[] args) throws Exception{ // The server: // 1. Create a ServerSocket object and specify the port number (6666) ServerSocket ss = new ServerSocket(6666); // Cyclic receive request while (true){ // 2. Call the accept() method to receive the client request and get the Socket object final Socket socket = ss.accept(); System.out.println("New client: " + socket.getPort()); // As long as the connection is established, a thread is opened to upload files new Thread(new Runnable() { public void run() { try{ // 3. Obtain the input stream through the returned Socket object InputStream is = socket.getInputStream(); // 4. Create byte output stream object and associate destination file path FileOutputStream fos = new FileOutputStream("d:\\img\\"+System.currentTimeMillis()+".jpg"); // 5. Define a byte array to store the read byte data byte[] bys = new byte[8192]; // 5. Define an int variable to store the number of bytes read int len; Thread.sleep(10000); // 6. Read data circularly while ((len = is.read(bys)) != -1) {// Stuck // 7. In the loop, write out the data fos.write(bys, 0, len); } System.out.println("============Start writing back data to the client=========="); // 8. Obtain the output stream through the returned Socket object OutputStream os = socket.getOutputStream(); // 9. Write out string data to the client os.write("File uploaded successfully!".getBytes()); // 10. Close the flow and release resources fos.close(); socket.close(); //ss.close(); }catch (Exception e){ } } }).start(); } } }
TCP emulation web server
- Accessing resources in a local project from a browser (similar to Tomcat)
// Server public class Server { public static void main(String[] args) throws Exception { // The server: // 1. Create a ServerSocket object and specify the port number (6666) ServerSocket ss = new ServerSocket(6666); while (true) { // 2. Call the accept() method to get the request and get the Socket object final Socket socket = ss.accept(); // Connection establishment, thread development new Thread(new Runnable() { public void run() { try { // 3. Obtain the input stream through the returned Socket object InputStream is = socket.getInputStream(); // 4. Through the input stream, go to the connection channel to obtain data, filter, and filter out the page path that the browser needs to access // 4.1 convert byte input stream to character input stream InputStreamReader isr = new InputStreamReader(is); // 4.2 read the data in the first row. Because we need to read the url request, we use BufferedReader to read the data line by line BufferedReader br = new BufferedReader(isr); String line = br.readLine(); // When we request a url, the request received by the server is: request method (GET) + path of our request + HTTP version System.out.println(line);// GET /socketnio/web/index.html HTTP/1.1 // 4.3 intercept the page path that the browser needs to visit, that is, we need to visit the index page under the web under socketnio // Separate into an array according to the space, then take the element with index 1, and intercept it from the position with index 1 of this element String path = line.split(" ")[1].substring(1); System.out.println(path);// socketnio/web/index.html // After getting our path, it's equivalent to file transfer // 5. Create a byte input stream object and associate the page path to be accessed FileInputStream fis = new FileInputStream(path); // 6. Obtain the output stream through the Socket object OutputStream os = socket.getOutputStream(); // 7. Define a byte array to store the read byte data byte[] bys = new byte[8192]; // 7. Define an int variable to store the number of bytes read int len; // When responding to the page, you need to send the following responses to the browser at the same time os.write("HTTP/1.1 200 OK\r\n".getBytes()); os.write("Content-Type:text/html\r\n".getBytes()); os.write("\r\n".getBytes()); // 8. Read data circularly while ((len = fis.read(bys)) != -1) { // 9. Write data in the loop os.write(bys, 0, len); } // 10. Close the flow and release resources fis.close(); socket.close(); //ss.close(); } catch (Exception e) { } } }).start(); } } }
NIO
Synchronous asynchronous, blocking and non blocking
Before learning the NIO flow of Java, first understand several keywords
-
Synchronization and asynchronism (synchronous/asynchronous): synchronization is a reliable and orderly operation mechanism. When we perform synchronous operations, the next task is to wait for the current call to return, and then proceed to the next step. Asynchronous, on the contrary, other tasks do not need to wait for the current call to return, usually rely on events, callbacks and other mechanisms to achieve order relations between tasks.
- Synchronization: after calling the method, you must get a return value. For example, if you buy a train ticket, you must buy a ticket before you can continue to the next step
- Asynchronous: after calling the method, there will be no return value, but there will be a callback function. The callback function refers to the method that will be executed automatically after meeting the conditions. For example, if you buy a train ticket, you don't have to buy a ticket. I can tell the conductor that when there is a ticket, you can help me issue a ticket
-
Blocking and non blocking: during blocking operation, the current thread will be in blocking state and cannot engage in other tasks. It can continue only when the conditions are ready, such as the establishment of a new connection to ServerSocket, or the completion of data reading and writing operations; Non blocking is to return directly regardless of whether the IO operation ends or not, and the corresponding operation continues to be processed in the background
- Blocking: if the purpose of the method is not achieved, it will always stop there (wait). For example, the accept method of ServerSocket
- Non blocking: the method is executed directly (without waiting) regardless of whether it has achieved its goal or not
NIO is synchronous because the kernel I/O operations of its accept/read/write method will block the current thread
Three components
NIO has three main components: Buffer, Channel and Selector. NIO is only used when the number of accesses is very large. For example, there will be high concurrency and a large number of connections in popular software or popular games
Buffer class
buffer
Three creation methods
// Method 1: create buffer in heap: indirect buffer -------------- > recommended ByteBuffer b1 = ByteBuffer.allocate(10); // Method 2: create a buffer in the system memory: direct buffer ByteBuffer b2 = ByteBuffer.allocateDirect(10); // Method 3: create a buffer in the heap byte[] bys = new byte[10]; ByteBuffer b3 = ByteBuffer.wrap(bys);
// Reading the source code, you can see that allocate is the HeapByteBuffer called, so it is a heap memory creation public static ByteBuffer allocate(int capacity) { if (capacity < 0) throw createCapacityException(capacity); return new HeapByteBuffer(capacity, capacity, null); } // The wrap method is actually called HeapByteBuffer public static ByteBuffer wrap(byte[] array, int offset, int length) { try { return new HeapByteBuffer(array, offset, length, null); } catch (IllegalArgumentException x) { throw new IndexOutOfBoundsException(); } }
- And a byte [] array is encapsulated inside the ByteBuffer class
- The creation and destruction efficiency of indirect buffer is higher than that of direct buffer
- Indirect buffers are less efficient than direct buffers
Main methods
put,capacity,limit,position,mark,reset,clear,flip
- Mainly understand clear and flip
package com.liu; import java.nio.ByteBuffer; import java.util.Arrays; public class Demo01 { public static void main(String[] args) { ByteBuffer buffer = ByteBuffer.allocate(10); // buffer capacity= 10 System.out.println("buffer of capacity= " + buffer.capacity()); buffer.put((byte) 1); buffer.put((byte) 2); // buffer= [1, 2, 0, 0, 0, 0, 0, 0, 0, 0] System.out.println("buffer= " + Arrays.toString(buffer.array())); // As the name implies, the buffer is limited to n(3) and N < = capacity buffer.limit(3); buffer.put((byte) 3); // buffer.put((byte)4); Exception: java.nio.bufferoverflow exception // buffer limit(3)= [1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] System.out.println("buffer stay limit(3)= " + Arrays.toString(buffer.array())); // Reposition the index position (equivalent to a pointer operating the array index, and position is to change the pointer position). The next operation will start from index n(1), and N < = n in limit buffer.position(1); buffer.put((byte) 4); buffer.put((byte) 5); // buffer position(1)= [1, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] System.out.println("buffer stay position(1)= " + Arrays.toString(buffer.array())); // mark and reset are together // mark marks the position (that is, the position of the array index pointer) // reset repositions the pointer to the position marked by mark, that is, 1 buffer.position(1); buffer.mark(); buffer.put((byte) 6); buffer.put((byte) 7); // buffer after position(1) mark= [1, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] System.out.println("buffer stay position(1)after mark= " + Arrays.toString(buffer.array())); buffer.reset(); buffer.put((byte) 8); // buffer after position(1), mark after reset= [1, 8, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] System.out.println("buffer stay position(1)after mark after reset= " + Arrays.toString(buffer.array())); // flip gives the pointer position to the position index to limit, and position is set to 0 // flip is mostly used to read and write files. You can write as much as you read buffer.limit(10);// Remove the limit limit first buffer.position(3); buffer.flip();// position=0, limit=3 buffer.put((byte) 9); buffer.put((byte) 10); buffer.put((byte) 11); // buffer.put((byte) 12); Exception: java.nio.BufferOverflowException // buffer = flip [9, 10, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0] System.out.println("buffer stay flip= " + Arrays.toString(buffer.array())); // Reset position=0 and limit=capacity buffer.clear(); for (int i = 0; i < 10; i++) { buffer.put((byte) i); } // buffer in clear= [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] System.out.println("buffer stay clear= " + Arrays.toString(buffer.array())); } }
Channel interface
passageway
- A Channel for reading and writing files. It can be regarded as input and output streams in IO. The difference is that the Channel is bidirectional, readable and writable
classification
- FileChannel: input and output streams for reading data from files
- Datagram channel: read and write UDP network protocol data
- SocketChannel: read and write TCP network protocol data socket (client)
- ServerSocketChannel: can listen for TCP connections to ServerSocket (server side)
FileChannel class
You can get a subclass object of FileInputStream and FileOutputStream through the getChannel() method
Copy of files
public static void main(String[] args) throws Exception { // Requirement: copy a.jpg file // Idea: use FileChannel to read the data into the byte buffer array, and then write it from the byte buffer array to the destination file // 1. Create byte input stream object and associate data source file path FileInputStream fis = new FileInputStream("d:\\img\\a.jpg"); // 2. Create byte output stream object and associate destination file path FileOutputStream fos = new FileOutputStream("d:\\img\\b.jpg"); // 3. Obtain the FileChannel object through the input stream object FileChannel c1 = fis.getChannel(); // 3. Obtain the FileChannel object through the output stream object FileChannel c2 = fos.getChannel(); // 4. Create a byte buffer array to store the read byte data ByteBuffer b = ByteBuffer.allocate(8192); // 5. Read byte data circularly while (c1.read(b) != -1) { // flip: position becomes: 0, and limit becomes: position(8192, but the last time may not be 8192). To ensure that the read data is written out // After using flip, you don't need to define a len to intercept. You can refer to the above TCP file operation b.flip(); // 6. In the loop, write out byte data c2.write(b); // clear: position becomes: 0,limit becomes: capacity(8192) for the next cycle b.clear(); } // 7. Close the flow and release resources c2.close(); c1.close(); fos.close(); fis.close(); }
FileChannel combined with MappedByteBuffer to achieve efficient read and write
MappedByteBuffer class is a ByteBuffer subclass. It can directly map files to memory and change the reading and writing in hard disk into reading and writing in memory, so it can improve the reading and writing efficiency of large files
- The map() method of FileChannel gets a MappedByteBuffer
- MappedByteBuffer map(MapMode mode, long position, long size). Note: map the size bytes from position in the node to the returned mappedbytebuffer.
- FileChannel.MapMode.READ_ONLY: the obtained image can only be read but not written
- FileChannel.MapMode.READ_WRITE: the resulting image is readable and writable
- FileChannel.MapMode.PRIVATE: get a private image
- Obtain Channel by using RandomAccessFile
- The Channel obtained by using the input stream can only specify the read mode, that is, "r"
- The Channel obtained by using the output stream can only specify the write mode, that is, "rw"
- Only the Channel obtained by RandomAccessFile can enable any of these three modes
public static void main(String[] args) throws Exception { long a = System.currentTimeMillis(); // 1. Create RandomAccessFile object: r means read, rw means read and write RandomAccessFile r1 = new RandomAccessFile("d:\\img\\a.rar", "r"); RandomAccessFile r2 = new RandomAccessFile("d:\\img\\b.rar", "rw"); // 2. Obtain Channel object FileChannel c1 = r1.getChannel(); FileChannel c2 = r2.getChannel(); // 3. Get the byte size of the source file long size = c1.size(); System.out.println(size); // Circular replication // Total file size: size // Assume the byte size of each copy: everySize = 500MB long everySize = 500 * 1024 * 1024; // How many copies are needed in total: count = size% everysize = = 0? size / everySize : size / everySize +1 long count = size % everySize == 0 ? size / everySize : size / everySize + 1; // Circular replication for (long i = 0; i < count; i++) { // Start byte position of each copy: start = i * everySize long start = i * everySize; // How many bytes are actually copied each time: truesize = size - Start > everysize? everySize : size - start; // This is mainly because the last replication is not equal to the size of each replication everySize long trueSize = size - start > everySize ? everySize : size - start; // 3. Use the Channel to call the map method to obtain the MappedByteBuffer MappedByteBuffer m1 = c1.map(FileChannel.MapMode.READ_ONLY, start, trueSize); MappedByteBuffer m2 = c2.map(FileChannel.MapMode.READ_WRITE, start, trueSize); // 4. Copy the byte data in m1 array to m2 for (long j = 0; j < trueSize; j++) { //System.out.println("j = " + j); // Get bytes in m1 byte b = m1.get(); // Store the obtained bytes in m2 m2.put(b); } } // 5. Release resources c2.close(); c1.close(); r2.close(); r1.close(); long b = System.currentTimeMillis(); System.out.println("time consuming: " + (b -a)/1000 + "second"); }
SocketChannel and ServerSocketChannel
-
The SocketChannel class is used to connect clients. It is equivalent to: Socket
-
The ServerSocketChannel class is used to connect to the server. It is equivalent to: ServerSocket
/** * client */ // Access channel SocketChannel sc = SocketChannel.open(); // Connect server sc.connect(new InetSocketAddress("127.0.0.1", 6666)); // Create ByteBuffer byte buffer array ByteBuffer b = ByteBuffer.allocate(1024); // Stores data in a byte buffer array b.put("The server,Hello,Do you have an appointment tonight?".getBytes()); // Position is set to 0 and limit is set to the previous position b.flip(); // Write data to server sc.write(b); // Release resources sc.close(); /** * Server */ // Get ServerSocketChannel ServerSocketChannel ssc = ServerSocketChannel.open(); // Binding port number ssc.bind(new InetSocketAddress(6666)); // Set non blocking ssc.configureBlocking(false); // Receive request SocketChannel sc = null; while (true) { // Receive client connections sc = ssc.accept(); // It won't block here if (sc == null){ System.out.println("Not connected yet"); // Loop until the client starts the connection }else{ System.out.println("The connection succeeded!"); break; } } // Create ByteBuffer byte buffer array ByteBuffer b = ByteBuffer.allocate(1024); // Receive data written by the client int len = sc.read(b); System.out.println(new String(b.array(),0,len)); // Release resources ssc.close();
Selector
selector
- Multichannel means that the server side listens to multiple "ports" at the same time. Each port listens to multiple client connections
- Multiplexing means that a Selector can listen to multiple server ports
- Server side non multiplexing effect
If "multiplexing" is not used, the server needs to open many threads to process the requests of each port. If it is in a high concurrency environment, the system performance will be degraded.
- Server side multiplexing effect
Using multiplexing, only one thread can process multiple channels, reduce memory occupancy and CPU switching time. It has very important advantages in high concurrency and high-frequency business environment
-
A Selector can monitor events in multiple channels, reduce system burden and improve program execution efficiency
-
Selector selector = Selector.open()
-
channel object. register(Selector sel, int ops) method to implement registration
-
Parameter 1: selector
-
Parameter 2: what events are you interested in when listening to the Channel through the Selector
-
1. Connection ready – constant: SelectionKey.OP_CONNECT
2. Receive ready – constant: SelectionKey.OP_ACCEPT (ServerSocketChannel can only use this item when registering)
3. Read ready – constant: SelectionKey.OP_READ
4. Write ready – constant: SelectionKey.OP_WRITE
-
be careful
1. For ServerSocketChannel, only OP can be used during registration_ Accept, otherwise an exception is thrown.
2. The ServerSocketChannel must be set to non blocking -
The server creates three channels, listens to three ports at the same time, and registers the three channels in a selector
public static void main(String[] args) throws Exception{ // Get the ServerSocketChannel object of 3 ports ServerSocketChannel ssc1 = ServerSocketChannel.open(); ServerSocketChannel ssc2 = ServerSocketChannel.open(); ServerSocketChannel ssc3 = ServerSocketChannel.open(); // Binding port number ssc1.bind(new InetSocketAddress(6666)); ssc2.bind(new InetSocketAddress(7777)); ssc3.bind(new InetSocketAddress(8888)); // Set Channel to non blocking ssc1.configureBlocking(false); ssc2.configureBlocking(false); ssc3.configureBlocking(false); // Get selector Selector selector = Selector.open(); // Register the Channel with the selector ssc1.register(selector, SelectionKey.OP_ACCEPT); ssc2.register(selector, SelectionKey.OP_ACCEPT); ssc3.register(selector, SelectionKey.OP_ACCEPT); // Mark }
common method
- select,selectedKeys,keys
select the method that the server waits for the client to connect
- Blocking problem:
- It will be blocked until the first client is connected
- After connecting to the client, if the client is not processed, the method is in a non blocking state
- After connecting to the client, if the client is processed, the method will enter the blocking state again
// client public static void main(String[] args) throws Exception{ // Get SocketChannel object SocketChannel sc = SocketChannel.open(); // Connect server sc.connect(new InetSocketAddress("127.0.0.1", 6666)); // Create ByteBuffer byte buffer array ByteBuffer b = ByteBuffer.allocate(1024); // Stores data in a byte buffer array b.put("The server,Hello,Do you have an appointment tonight 6666?".getBytes()); // Position is set to 0 and limit is set to the previous position b.flip(); Thread.sleep(4000); // Write data to server sc.write(b); // Release resources sc.close(); }
// Duplicate location code marked from above while (true) { // The server is waiting for the client to connect selector.select(); // selectedKeys() method of Selector: get all connected channel collections // Get all connected Channel channels Set<SelectionKey> keys = selector.selectedKeys(); System.out.println("Number of all connected channels:"+keys.size()); // keys() method of Selector: get all registered channel collections // Get registered connection channel Set<SelectionKey> set = selector.keys(); System.out.println("Registered connection channel:"+set.size()); // Loop through the connected Channel for (SelectionKey key : keys) { // SelectionKey encapsulates ServerSocketChannel // Get the ServerSocketChannel to which the client wants to connect ServerSocketChannel channel = (ServerSocketChannel)key.channel(); // Processing client requests SocketChannel sc = channel.accept(); // sc.read.... } }
- In fact, the selector is equivalent to a middleman. Let's look at this figure
NIO multiplexing
public class Server { public static void main(String[] args) throws Exception{ // Get the ServerSocketChannel object of 3 ports ServerSocketChannel ssc1 = ServerSocketChannel.open(); ServerSocketChannel ssc2 = ServerSocketChannel.open(); ServerSocketChannel ssc3 = ServerSocketChannel.open(); // Binding port number ssc1.bind(new InetSocketAddress(6666)); ssc2.bind(new InetSocketAddress(7777)); ssc3.bind(new InetSocketAddress(8888)); // Set Channel to non blocking ssc1.configureBlocking(false); ssc2.configureBlocking(false); ssc3.configureBlocking(false); // Get selector Selector selector = Selector.open(); // Register the Channel with the selector ssc1.register(selector, SelectionKey.OP_ACCEPT); ssc2.register(selector, SelectionKey.OP_ACCEPT); ssc3.register(selector, SelectionKey.OP_ACCEPT); while (true) { // The server is waiting for the client to connect selector.select(); // Processing client requests // Get all connected Channel channels Set<SelectionKey> keys = selector.selectedKeys(); /** * Problem: the Selector puts all connected server objects in a Set collection, but does not delete them after use, resulting in traversing the collection to the point where they have been used * Like, something's wrong * Solution: after use, it should be deleted from the collection. Because it cannot be deleted while traversing, use the iterator for traversal */ // Loop through all connected Channel channels /*for (SelectionKey key : keys) { // SelectionKey Encapsulated ServerSocketChannel // Convert key to ServerSocketChannel object ServerSocketChannel ssc = (ServerSocketChannel)key.channel(); // Processing client requests SocketChannel sc = ssc.accept(); // Read the data written by the client ByteBuffer b = ByteBuffer.allocate(1024); int len = sc.read(b); System.out.println(new String(b.array(),0,len)); sc.close(); }*/ // Get iterator Iterator<SelectionKey> it = keys.iterator(); while (it.hasNext()) { SelectionKey key = it.next(); // Convert key to ServerSocketChannel object ServerSocketChannel ssc = (ServerSocketChannel)key.channel(); // Processing client requests SocketChannel sc = ssc.accept(); // Read the data written by the client ByteBuffer b = ByteBuffer.allocate(1024); int len = sc.read(b); System.out.println(new String(b.array(),0,len)); sc.close(); // delete it.remove(); } } } }
Multiplex finishing
- Server
public class NIOServer { private int port = 8888; // For character set encoding and decoding private Charset charset = Charset.forName("UTF-8"); // Buffer for receiving data private ByteBuffer rBuffer = ByteBuffer.allocate(1024); // Buffer for sending data private ByteBuffer sBuffer = ByteBuffer.allocate(1024); // Used to store the client SocketChannel collection private Map<String, SocketChannel> clientMap = new HashMap(); // Used to listen for channel events private static Selector selector; public NIOServer(int port) { this.port = port; try { init(); } catch (IOException e) { e.printStackTrace(); } } // initialize server private void init() throws IOException { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.configureBlocking(false); ServerSocket serverSocket = serverSocketChannel.socket(); serverSocket.bind(new InetSocketAddress(port)); selector = Selector.open(); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("Server startup, port:" + port); } /** * The server side polls and listens. The select method is blocked until a related event occurs or a timeout occurs */ public void listen() { while (true) { try { selector.select(); // The return value is the number of events triggered this time Set<SelectionKey> selectionKeys = selector.selectedKeys(); //selectionKeys.forEach(selectionKey -> handle(selectionKey)); for(SelectionKey sk : selectionKeys){ handle(sk); } selectionKeys.clear(); // Clear processed events } catch (IOException e) { e.printStackTrace(); } } } /** * Handling events */ private void handle(SelectionKey selectionKey) { try { // There are clients to connect to if (selectionKey.isAcceptable()) { ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel(); SocketChannel client = server.accept(); client.configureBlocking(false); client.register(selector, SelectionKey.OP_READ); clientMap.put(getClientName(client), client); } // The client sent a message else if (selectionKey.isReadable()) { SocketChannel client = (SocketChannel) selectionKey.channel(); rBuffer.clear(); int bytes = client.read(rBuffer); if (bytes > 0) { rBuffer.flip(); String receiveText = String.valueOf(charset.decode(rBuffer)); System.out.println(client.toString() + ":" + receiveText); dispatch(client, receiveText); } } } catch (IOException e) { e.printStackTrace(); } } /** * Forward messages to each client */ private void dispatch(SocketChannel client, String info) throws IOException { if (!clientMap.isEmpty()) { for (Map.Entry<String, SocketChannel> entry : clientMap.entrySet()) { SocketChannel temp = entry.getValue(); if (!client.equals(temp)) { sBuffer.clear(); sBuffer.put(charset.encode(getClientName(client) + ":" + info)); sBuffer.flip(); temp.write(sBuffer); } } } } /** * Generate client name */ private String getClientName(SocketChannel client){ Socket socket = client.socket(); return "[" + socket.getInetAddress().toString().substring(1) + ":" + Integer.toHexString(client.hashCode()) + "]"; } public static void main(String[] args) { NIOServer server = new NIOServer(7777); server.listen(); } }
- client
public class NIOClient { // Server address private InetSocketAddress SERVER; // Buffer for receiving data private ByteBuffer rBuffer = ByteBuffer.allocate(1024); // Buffer for sending data private ByteBuffer sBuffer = ByteBuffer.allocate(1024); // Used to listen for channel events private static Selector selector; // buffer for encoding / decoding private Charset charset = Charset.forName("UTF-8"); public NIOClient(int port) { SERVER = new InetSocketAddress("localhost", port); try { init(); } catch (IOException e) { e.printStackTrace(); } } // Initialize client private void init() throws IOException { SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); selector = Selector.open(); socketChannel.register(selector, SelectionKey.OP_CONNECT); socketChannel.connect(SERVER); while (true) { selector.select(); Set<SelectionKey> selectionKeys = selector.selectedKeys(); //selectionKeys.forEach(selectionKey -> handle(selectionKey)); for(SelectionKey sk : selectionKeys){ handle(sk); } selectionKeys.clear(); // Clear processed events } } /** * Handling events */ private void handle(SelectionKey selectionKey) { try { // Connection ready event if (selectionKey.isConnectable()) { SocketChannel client = (SocketChannel) selectionKey.channel(); if (client.isConnectionPending()) { client.finishConnect(); System.out.println("Connection succeeded!"); // Start the thread to listen for client input new Thread() { @Override public void run() { while (true) { try { sBuffer.clear(); Scanner scanner = new Scanner(System.in); String sendText = scanner.nextLine(); System.out.println(sendText); sBuffer.put(charset.encode(sendText)); sBuffer.flip(); client.write(sBuffer); } catch (IOException e) { e.printStackTrace(); } } } }.start(); } // Register readable events client.register(selector, SelectionKey.OP_READ); } // Readable events, with information sent from the server, read and output to the screen else if (selectionKey.isReadable()) { SocketChannel client = (SocketChannel) selectionKey.channel(); rBuffer.clear(); int count = client.read(rBuffer); if (count > 0) { String receiveText = new String(rBuffer.array(), 0, count); System.out.println(receiveText); } } } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { new NIOClient(7777); } }