Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

The method of realizing Socket programming based on Java

2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly introduces "the method of realizing Socket programming based on Java". In the daily operation, I believe that many people have doubts about the method of realizing Socket programming based on Java. The editor consulted all kinds of materials and sorted out simple and easy-to-use operation methods. I hope it will be helpful to answer the doubts of "Socket programming method based on Java". Next, please follow the editor to study!

Get to know Socket

Socket, also known as socket, is a protocol, convention or specification for network communication between different processes.

For socket programming, it is more like a layer of encapsulation or abstraction based on protocols such as TCP/UDP, and it is a set of interfaces provided by a system for programming related to network communication.

Establish the basic process of socket

Let's take the basic api provided by the linux operating system as an example to understand the basic process of establishing a socket communication:

You can see that socket is essentially a simplification and abstraction of tcp connections (and, of course, other connections such as udp) at the programming level.

1. Basic Socket demonstration 1.1 one-way communication

First, let's start with the basic socket code that sends and receives messages only once:

Server:

Package com.marklux.socket.base;import java.io.IOException;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;/** * The very basic socket server that only listen one single message. * / public class BaseSocketServer {private ServerSocket server; private Socket socket; private int port; private InputStream inputStream; private static final int MAX_BUFFER_SIZE = 1024; public int getPort () {return port;} public void setPort (int port) {this.port = port;} public BaseSocketServer (int port) {this.port = port } public void runServerSingle () throws IOException {this.server = new ServerSocket (this.port); System.out.println ("base socket server started."); / / the code will block here till the request come. This.socket = server.accept (); this.inputStream = this.socket.getInputStream (); byte [] readBytes = new byte [Max _ BUFFER_SIZE]; int msgLen; StringBuilder stringBuilder = new StringBuilder (); while ((msgLen = inputStream.read (readBytes))! =-1) {stringBuilder.append (readBytes,0,msgLen (readBytes,0,msgLen, "UTF-8")) } System.out.println ("get message from client:" + stringBuilder); inputStream.close (); socket.close (); server.close ();} public static void main (String [] args) {BaseSocketServer bs = new BaseSocketServer (9799); try {bs.runServerSingle ();} catch (IOException e) {e.printStackTrace () }}}

Client:

Package com.marklux.socket.base;import java.io.IOException;import java.io.OutputStream;import java.io.UnsupportedEncodingException;import java.net.Socket;/** * The very basic socket client that only send one single message. * / public class BaseSocketClient {private String serverHost; private int serverPort; private Socket socket; private OutputStream outputStream; public BaseSocketClient (String host, int port) {this.serverHost = host; this.serverPort = port;} public void connetServer () throws IOException {this.socket = new Socket (this.serverHost, this.serverPort); this.outputStream = socket.getOutputStream () / / why the output stream?} public void sendSingle (String message) throws IOException {try {this.outputStream.write (message.getBytes ("UTF-8"));} catch (UnsupportedEncodingException e) {System.out.println (e.getMessage ());} this.outputStream.close (); this.socket.close () } public static void main (String [] args) {BaseSocketClient bc = new BaseSocketClient ("127.0.0.1", 9799); try {bc.connetServer (); bc.sendSingle ("Hi from mark.");} catch (IOException e) {e.printStackTrace ();}

First run the server, and then run the client, you can see the effect.

Note that the implementation of the IO operation here, we use a size of MAX_BUFFER_SIZE byte array as a buffer, and then take bytes from the input stream into the buffer, and then take bytes from the buffer to build into the string, which is very useful when the input stream file is very large, in fact, the NIO I will talk about later is also based on this idea.

1.2 two-way communication

The above example implements only one-way communication, which is obviously a bit of a waste of channels. The socket connection supports full-duplex two-way communication (the underlying is tcp). In the following example, the server returns a receipt to the client after receiving a message from the client.

And we use some java.io-wrapped methods to simplify the entire communication process (because the message length is small, buffers are no longer used).

Server:

Public void runServer () throws IOException {this.serverSocket = new ServerSocket (port); this.socket = serverSocket.accept (); this.inputStream = socket.getInputStream (); String message = new String (inputStream.readAllBytes (), "UTF-8"); System.out.println ("received message:" + message); this.socket.shutdownInput () / / tell the client that the reception has been completed, and then only send / / write the receipt. This.outputStream = this.socket.getOutputStream (); String receipt = "We received your message:" + message; outputStream.write (receipt.getBytes ("UTF-8")); this.outputStream.close (); this.socket.close ();}

Client:

Public void sendMessage (String message) throws IOException {this.socket = new Socket (host,port); this.outputStream = socket.getOutputStream (); this.outputStream.write (message.getBytes ("UTF-8")); this.socket.shutdownOutput (); / / tells the server that all sending actions have ended, and then can only receive this.inputStream = socket.getInputStream () String receipt = new String (inputStream.readAllBytes (), "UTF-8"); System.out.println ("got receipt:" + receipt); this.inputStream.close (); this.socket.close ();}

Note that after receiving the message on the server and sending the message on the client, we call shutdownInput () and shutdownOutput () instead of the stream corresponding to close directly. This is because shutting down any stream will directly lead to the shutdown of socket, so it is impossible to send the receipt later.

Note, however, that after calling shutdownInput () and shutdownOutput (), the corresponding stream is also closed and cannot be sent / written to socket again.

two。 Send more messages: definition of the end

In the two examples just now, each time the stream is opened, only one write / read operation can be performed, and when the corresponding stream is closed, it cannot be written / read again.

In this case, if you want to send messages twice, you have to set up two socket, which is both resource-consuming and troublesome. In fact, we can not close the corresponding stream at all, as long as we write the message in stages.

But in that case, we have to face another problem: how to judge the end of a message?

2.1 use special symbols

The easiest way is to use some special symbols to mark the completion of a transmission, the server can complete a read as long as the corresponding symbol is read, and then carry out related processing operations.

In the following example, we use the newline character\ nto mark the end of a transmission. Every time the server receives a message, it prints it, and uses Scanner to simplify the operation:

Server:

Public void runServer () throws IOException {this.server = new ServerSocket (this.port); System.out.println ("base socket server started."); this.socket = server.accept (); / / the code will block here till the request come. This.inputStream = this.socket.getInputStream (); Scanner sc = new Scanner (this.inputStream); while (sc.hasNextLine ()) {System.out.println ("get info from client:" + sc.nextLine ());} / / receive and output message content this.inputStream.close (); socket.close ();}

Client:

Public void connetServer () throws IOException {this.socket = new Socket (this.serverHost, this.serverPort); this.outputStream = socket.getOutputStream ();} public void send (String message) throws IOException {String sendMsg = message + "\ n"; / / we mark\ n as an end of line. Try {this.outputStream.write (sendMsg.getBytes ("UTF-8"));} catch (UnsupportedEncodingException e) {System.out.println (e.getMessage ());} / / this.outputStream.close (); / / this.socket.shutdownOutput ();} public static void main (String [] args) {CycleSocketClient cc = new CycleSocketClient ("127.0.0.1", 9799) Try {cc.connetServer (); Scanner sc = new Scanner (System.in); while (sc.hasNext ()) {String line = sc.nextLine (); cc.send (line);}} catch (IOException e) {e.printStackTrace ();}}

The effect after running is that every time the client enters a line of text and presses enter, the server will print out the corresponding message reading record.

2.2 defined by length

Going back to the origin, the reason why we can't locate when the message ends is because we can't determine the length of each message.

In fact, the length of the message can be sent first, and when the server knows the length of the message, it can complete the reception of the message.

In general, sending a message becomes two steps

The length of the message sent

Send a message

The final problem is that the number of bytes sent by the "length of message sent" step must be fixed, otherwise we will still be deadlocked.

Generally speaking, we can use a fixed number of bytes to save the length of the message, for example, stipulating that the first 2 bytes is the length of the message, but in this way, the maximum length of the message that we can send is also fixed. Take 2 bytes as an example, the maximum length of the message we send is no more than 2 ^ 16 bytes or 64K.

If you know the encoding of some characters, you will know that we can actually use variable space to store the length of the message, such as:

The first bit of the first byte is 0: 0XXXXXXX, which means that the length is only one byte, and the maximum is 128b.

The first byte of the first byte is 110, then the following byte is attached to indicate the length: that is, 110XXXXX 10XXXXXX, with a maximum of 2048, which means 2K

The first bit of the first byte is 1110, then the next two bytes are attached to indicate the length: 110XXXXX 10XXXXXX 10XXXXXX, with a maximum of 131072, indicating 128K

And so on.

Of course, this will be a bit troublesome to implement, so in the following example we still use a fixed two bytes to record the length of the message.

Server:

Public void runServer () throws IOException {this.serverSocket = new ServerSocket (this.port); this.socket = serverSocket.accept (); this.inputStream = socket.getInputStream (); byte [] bytes; while (true) {/ / read the first byte int first = inputStream.read () If (first = =-1) {/ / if it is-1, the input stream has been closed, so there is no need to listen to this.socket.close (); break;} / / reads the second byte int second = inputStream.read () Int length = (first > 8); / / write transmits only one byte of this.outputStream.write (length) at a time by default; / / after the length is transferred, the message this.outputStream.write (bytes) is formally transmitted;} public static void main (String [] args) {LengthSocketClient lc = new LengthSocketClient ("127.0.0.1", 9799) Try {lc.connetServer (); Scanner sc = new Scanner (System.in); while (sc.hasNextLine ()) {lc.sendMessage (sc.nextLine ());}} catch (IOException e) {e.printStackTrace ();}} 3. Handle more connections: multithread 3.1 to send and receive messages at the same time

Before we consider that the server handles multiple connections, let's consider using multithreading to modify the original one-to-one conversation example.

In the original example, the receiver of the message cannot actively send messages to each other, in other words, we do not really talk to each other, mainly because the sending and receiving of messages cannot be done at the same time, so we need to use two threads, one of which is used to listen for keyboard input and write it to socket, and the other is responsible for listening to socket and displaying the received messages.

For simplicity, we directly let the main thread be responsible for keyboard listening and message sending, while opening another thread to pull the message and display it.

Message pull thread ListenThread.java

Public class ListenThread implements Runnable {private Socket socket; private InputStream inputStream; public ListenThread (Socket socket) {this.socket = socket;} @ Override public void run () throws RuntimeException {try {this.inputStream = socket.getInputStream ();} catch (IOException e) {e.printStackTrace (); throw new RuntimeException (e.getMessage ()) } while (true) {try {int first = this.inputStream.read (); if (first = =-1) {/ / the input stream has been closed, so there is no need to continue reading throw new RuntimeException ("disconnected.") } int second = this.inputStream.read (); int msgLength = (first8); outputStream.write (length); outputStream.write (msgBytes);} public static void main (String [] args) {Scanner scanner = new Scanner (System.in); ChatSocket chatSocket = new ChatSocket () System.out.println ("select connect type: 1 for server and 2 for client"); int type = Integer.parseInt (scanner.nextLine (). ToString ()); if (type = = 1) {System.out.print ("input server port:"); int port = scanner.nextInt (); try {chatSocket.runAsServer (port) } catch (IOException e) {e.printStackTrace ();}} else if (type = = 2) {System.out.print ("input server host:"); String host = scanner.nextLine (); System.out.print ("input server port:"); int port = scanner.nextInt () Try {chatSocket.runAsClient (host, port);} catch (IOException e) {e.printStackTrace ();} 3.2 optimize server concurrency capabilities using thread pools

As a server, it would be a waste of resources to establish a socket connection with only one client at a time, so we can allow the server and multiple clients to establish multiple socket.

So since you have to deal with multiple connections, you have to face the concurrency problem (of course, you can also write loops to take turns). We can use multithreading to handle concurrency, but thread creation and destruction consume a lot of resources and time, so it's best to do it in one step and use a thread pool.

Here is an exemplary server-side code:

Public class SocketServer {public static void main (String args []) throws Exception {/ / listen on the specified port int port = 55533; ServerSocket server = new ServerSocket (port); / / server will always wait for the arrival of the connection System.out.println ("server will always wait for the arrival of the connection") / / if multithreading is used, a thread pool is required to prevent creating too many threads when concurrency is too high to exhaust resources ExecutorService threadPool = Executors.newFixedThreadPool (100); while (true) {Socket socket = server.accept () Runnable runnable= ()-> {after the connection is established, get the input stream from socket and set up a buffer to read InputStream inputStream = socket.getInputStream (); byte [] bytes = new byte [1024]; int len; StringBuilder sb = new StringBuilder () While ((len = inputStream.read (bytes))! =-1) {/ / pay attention to specify the encoding format. Sender and receiver must be unified. It is recommended to use UTF-8 sb.append (new String (bytes, 0, len, "UTF-8"));} System.out.println ("get message from client:" + sb); inputStream.close () Socket.close ();} catch (Exception e) {e.printStackTrace ();}}; threadPool.submit (runnable);}} 4. Connect to keep alive

I think it is not difficult for you to find a problem, that is, after the socket connection is successfully established, if an exception occurs midway and one party disconnects, the other party will not be able to find it. Only when you try to send / receive the message again will you quit because of throwing an exception.

To put it simply, the socket connection we maintain is a long connection, but we do not guarantee its timeliness. One second it may still work, but not the next.

4.1 use of heartbeats

The most common way to ensure that a connection is available at any time is to send a heartbeat packet regularly to detect whether the connection is normal. This is still very important for services with high real-time requirements (such as message push).

The general plan is as follows:

The two sides agreed on the format of the heartbeat packet, which should be different from ordinary messages.

The client sends a heartbeat packet to the server at regular intervals.

Every time the server receives a heartbeat packet, it discards it.

If one of the client's heartbeats fails to send, it can be judged that the connection has been disconnected

If the real-time requirement is very high, the server can also regularly check the frequency of heartbeat packets sent by the client. If it is not sent for a certain period of time, it can be considered that the connection has been disconnected.

4.2 reconnect when disconnected

The use of heartbeat packets will inevitably increase the burden of bandwidth and performance. For ordinary applications, it is not necessary to use this solution. If a connection exception is thrown when the message is sent, just try to reconnect.

Compared with the above scenario, the message that throws an exception acts as a heartbeat.

Generally speaking, whether and how to keep the connection alive needs to be flexibly thought and customized according to the specific business scenario.

At this point, the study of "the method of Socket programming based on Java" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report