Java: multithreading and UDP socket programming

I am new to multithreading and socket programming in Java. I would like to know that the best way to implement 2 threads is one for receiving a socket and one for sending a socket. If what I'm trying to do sounds absurd, pls let me know why! The code is largely inspired by Sun's online learning materials. I want to use Multicast sockets so that I can work with a multicast group.

class Server extends Thread { static protected MulticastSocket socket = null; protected BufferedReader in = null; public InetAddress group; private static class Receive implements Runnable { public void run() { try { byte[] buf = new byte[256]; DatagramPacket pkt = new DatagramPacket(buf,buf.length); socket.receive(pkt); String received = new String(pkt.getData(),0,pkt.getLength()); System.out.println("From server@ " + received); Thread.sleep(1000); } catch (IOException e) { System.out.println("Error:"+e); } catch (InterruptedException e) { System.out.println("Error:"+e); } } } public Server() throws IOException { super("server"); socket = new MulticastSocket(4446); group = InetAddress.getByName("239.231.12.3"); socket.joinGroup(group); } public void run() { while(1>0) { try { byte[] buf = new byte[256]; DatagramPacket pkt = new DatagramPacket(buf,buf.length); //String msg = reader.readLine(); String pid = ManagementFactory.getRuntimeMXBean().getName(); buf = pid.getBytes(); pkt = new DatagramPacket(buf,buf.length,group,4446); socket.send(pkt); Thread t = new Thread(new Receive()); t.start(); while(t.isAlive()) { t.join(1000); } sleep(1); } catch (IOException e) { System.out.println("Error:"+e); } catch (InterruptedException e) { System.out.println("Error:"+e); } } //socket.close(); } public static void main(String[] args) throws IOException { new Server().start(); //System.out.println("Hello"); } } 
+4
source share
4 answers

First of all: your classes should start with a capital letter on the Java Naming Conventions :

Class names must be nouns in the mixed case with the first letter, each inner word is capitalized. Try to keep your class names simple and descriptive. Use whole words - avoid abbreviations and abbreviations (unless the abbreviation is much wider than a long form, such as a URL or HTML).

Second: Try to break the code into sequential sections and organize them around some general function that you are dealing with ... perhaps around the functionality or model that you are programming.

The (basic) model for the server is that the only thing it does is socket connections to connect ... the server relies on a handler to handle these connections and that it is. If you try to build this model, it will look something like this:

 class Server{ private final ServerSocket serverSocket; private final ExecutorService pool; public Server(int port, int poolSize) throws IOException { serverSocket = new ServerSocket(port); pool = Executors.newFixedThreadPool(poolSize); } public void serve() { try { while(true) { pool.execute(new Handler(serverSocket.accept())); } } catch (IOException ex) { pool.shutdown(); } } } class Handler implements Runnable { private final Socket socket; Handler(Socket socket) { this.socket = socket; } public void run() { // receive the datagram packets } } 

Third: I would recommend that you look at some existing examples.

Updated for comments:
OK Ravi, there are some big problems with your code and some minor problems with it:

  • I assume that the Receive class is your client ... you should pull it out as a separate program (with your main class) and simultaneously start the server and several clients. Creating a new “client thread” from your server for each new UDP packet that you send is an alarming idea (a big problem).

  • When you create a client application, you must make it run the receive code in its while (minor issue), for example:

     public class Client extends Thread { public Client(/*..*/) { // initialize your client } public void run() { while(true) { // receive UDP packets // process the UDP packets } } public static void main(String[] args) throws IOException { // start your client new Client().start(); } } 
  • You only need one thread per client and one thread per server (you do not have a technical thread there that has its own thread), so you may not find an ExecutorService , which is useful.

Otherwise, your approach is correct ... but I would recommend that you familiarize yourself with some examples.

+9
source

The desire to create threads in the application is not absurd! You won’t need exactly 2 threads, but I think you are talking about two classes that implement the Runnable interface.

The threading API has gotten better with Java 1.5, and you no longer need to mess with java.lang.Thread. You can simply create java.util.concurrent.Executor and send Runnable instances to it.

The book Java Concurrency in Practice uses this exact problem - creating a multi-threaded socket server - and goes through several iterations of code to show the best way to do this. Check out the chapter in the free chapter, which is great. I will not copy / paste the code here, but look specifically at Listing 6.8.

+2
source

It’s good that the Eclipse story works even one day ago. Thanks to this, I can give both Ravi a working example and Lirik his answer to the leak.

Let me start by stating that I don’t know what causes this leak, but if I leave it long enough it will fail on OutOfMemoryError .

Secondly, I left the working code commented out for Ravi for the working base example of my UDP server. The timeout was there to check how long my firewall would kill the end of the receivers (30 seconds). Just delete everything with the pool and you are good to go.

So, here is a working, but leaking version of my example with a streaming UDP server.

 public class TestServer { private static Integer TIMEOUT = 30; private final static int MAX_BUFFER_SIZE = 8192; private final static int MAX_LISTENER_THREADS = 5; private final static SimpleDateFormat DateFormat = new SimpleDateFormat("yyyy-dd-MM HH:mm:ss.SSSZ"); private int mPort; private DatagramSocket mSocket; // You can remove this for a working version private ExecutorService mPool; public TestServer(int port) { mPort = port; try { mSocket = new DatagramSocket(mPort); mSocket.setReceiveBufferSize(MAX_BUFFER_SIZE); mSocket.setSendBufferSize(MAX_BUFFER_SIZE); mSocket.setSoTimeout(0); // You can uncomment this for a working version //for (int i = 0; i < MAX_LISTENER_THREADS; i++) { // new Thread(new Listener(mSocket)).start(); //} // You can remove this for a working version mPool = Executors.newFixedThreadPool(MAX_LISTENER_THREADS); } catch (IOException e) { e.printStackTrace(); } } // You can remove this for a working version public void start() { try { try { while (true) { mPool.execute(new Listener(mSocket)); } } catch (Exception e) { e.printStackTrace(); } } finally { mPool.shutdown(); } } private class Listener implements Runnable { private final DatagramSocket socket; public Listener(DatagramSocket serverSocket) { socket = serverSocket; } private String readLn(DatagramPacket packet) throws IOException { socket.receive(packet); return new BufferedReader(new InputStreamReader(new ByteArrayInputStream(packet.getData())), MAX_BUFFER_SIZE).readLine(); } private void writeLn(DatagramPacket packet, String string) throws IOException { packet.setData(string.concat("\r\n").getBytes()); socket.send(packet); } @Override public void run() { DatagramPacket packet = new DatagramPacket(new byte[MAX_BUFFER_SIZE], MAX_BUFFER_SIZE); String s; while (true) { try { packet = new DatagramPacket(new byte[MAX_BUFFER_SIZE], MAX_BUFFER_SIZE); s = readLn(packet); System.out.println(DateFormat.format(new Date()) + " Received: " + s); Thread.sleep(TIMEOUT * 1000); writeLn(packet, s); System.out.println(DateFormat.format(new Date()) + " Sent: " + s); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { if (args.length == 1) { try { TIMEOUT = Integer.parseInt(args[0]); } catch (Exception e) { TIMEOUT = 30; } } System.out.println(DateFormat.format(new Date()) + " Timeout: " + TIMEOUT); //new TestServer(4444); new TestServer(4444).start(); } } 

by the way. @Lirik, I first saw this behavior in Eclipse, after which I checked it from the command line. And again, I do NOT know what causes it;) sorry ...

+1
source

2 threads are excellent. One reader is another writer. Remember that with UDP you should not create new handler threads (unless you do this, it takes a lot of time), I recommend throwing incoming messages into the processing queue. The same for sending, there is a sending thread that blocks the incoming queue for sending UDP.

0
source

Source: https://habr.com/ru/post/1307609/


All Articles