⚠️
La gestión de las conexiones se hace en un único hilo, por lo que si dos peticiones de diferentes clientes llegan a la vez, una se perderá

TCP

ℹ️
Estos códigos contienen una clase donde he implementado diferentes métodos según las acciones posibles a través de los sockers

Servidor

package src;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class TCP_Server {
  private int port;
  ServerSocket serverSocket;
  Socket clientSocket;

  /**
   * Constructor. Creates a new server socket and binds it to the specified port. This method wont accept any incoming connections.
   * 
   * @param port The port to bind the server socket to
   * @throws IOException If an I/O error occurs
   */
  public TCP_Server(int port) throws IOException {
    this.port = port;

    serverSocket = new ServerSocket(1234);
  }

  /**
   * Accepts an incoming connection. This method will block until a connection is made.
   * 
   * @throws IOException If an I/O error occurs
   */
  public void accept() throws IOException {
    clientSocket = serverSocket.accept();
  }

  // ==========================================[ COMMUNICATION METHODS ]==================================================

  // ----------------------------------------[ Bytes methods ]--------------------------------------------------

  /**
   * Reads an array of bytes from the client. This method will block until the client sends all the expected bytes.
   * 
   * @param nBytes The number of bytes we want to read
   * @return The data sent by the client
   * @throws IOException
   */
  public byte[] recvBytes(int nBytes) throws IOException {
    InputStream recvPipe = clientSocket.getInputStream();

    // Buffer to store the data
    byte buffer[] = new byte[nBytes];
    // Read the data and store them in the buffer (returns the number of bytes read)
    int bytesRead = recvPipe.read(buffer);
    // Copy the data from the buffer to a new array of the exact size
    byte data[] = new byte[bytesRead];
    for (int i = 0; i < bytesRead; i++) {
      data[i] = buffer[i];
    }

    // If the server didn't send any data, return null
    if (bytesRead == -1) {
      return null;
    }
    // Return the data
    else {
      return data;
    }
  }

  /**
   * Sends an array of bytes to the client.
   * 
   * @param data The data we want to send as an array of bytes
   * @throws IOException If an I/O error occurs
   */
  public void sendBytes(byte[] data) throws IOException {
    // Send the data
    OutputStream outputStream = clientSocket.getOutputStream();
    outputStream.write(data);
  }

  // ----------------------------------------[ String methods ]--------------------------------------------------

  /**
   * Reads a string from the client. This method will block until the client sends the '\n' character.
   * 
   * @return The string sent by the client
   * @throws IOException
   */
  public String recvStringLine() throws IOException {
    InputStreamReader clientInput = new InputStreamReader(clientSocket.getInputStream());
    BufferedReader inputBuffer = new BufferedReader(clientInput);

    // Read the data and store them in the buffer (returns the number of bytes read)
    String line = inputBuffer.readLine();

    return line;
  }

  /**
   * Sends a string to the client.
   * 
   * @param msg The message we want to send as a string
   * @throws IOException If an I/O error occurs
   */
  public void sendString(String msg) throws IOException {
    // Send the data
    DataOutputStream outputStream = new DataOutputStream(clientSocket.getOutputStream());
    outputStream.writeBytes(msg);
  }

  // ----------------------------------------[ File methods ]--------------------------------------------------

  /**
   * Sends a file to the client.
   * 
   * @param path
   * @throws IOException
   */
  public void sendFile(String path) throws IOException {
    // Writter to send the file over the socket
    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
    // Read the file
    BufferedReader reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(path)));

    System.out.println("Sending file: " + path);
    // Read the file line by line and send it to the client
    String line;
    while ((line = reader.readLine()) != null) {
      writer.write(line);
    }
    System.out.println("File sent.");

    // Close the file
    reader.close();
    writer.close();
  }

  // ==========================================[ CLOSE ]==================================================
  /**
   * Closes the server socket.
   * 
   * @throws IOException
   */
  public void closeServer() throws IOException {
    serverSocket.close();
  }

  /**
   * Closes the client socket.
   * 
   * @throws IOException
   */
  public void closeClient() throws IOException {
    clientSocket.close();
  }

  // ==========================================[ SERVER OPERATION ]==================================================
  // Premade methods to be used by the server when a some data is received from the client

  public void serverOperation(String sentence) throws IOException {
    String newSentence = sentence.toUpperCase() + '\n';
    sendString(newSentence);
  }

  public void serverOperation(byte[] data) throws IOException {
    for (int i = 0; i < data.length; i++) {
      data[i] = (byte) (data[i] + 1);
    }
    sendBytes(data);
  }

  // ==========================================[ DEBUG METHODS ]==================================================

  /**
   * The purpose of this method is to debug the server. It will echo the uppercase of the received string back to the client.
   * 
   * @throws IOException If an I/O error occurs
   */
  public void echoLines() throws IOException {
    while (true) {
      try {
        // Accept the incoming connection
        accept();
        System.out.println("Connection accepted...");

        // Receive the data
        String sentence = recvStringLine();

        // OPERATION DONE AT THE SERVER
        serverOperation(sentence);
        // ----------------------------

        // Close the connection (send to the client the '-1' message to close the connection)
        clientSocket.close();

      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }

  /**
   * The purpose of this method is to debug the server. It will echo the received (byte+1) back to the client.
   * 
   * @throws IOException If an I/O error occurs
   */
  public void echoByte() throws IOException {
    while (true) {
      try {
        // Accept the incoming connection
        accept();
        System.out.println("Connection accepted...");

        // Receive the data
        byte data[] = recvBytes(1);

        // OPERATION DONE AT THE SERVER
        serverOperation(data);
        // ----------------------------

        // Close the connection (send to the client the '-1' message to close the connection)
        clientSocket.close();

      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }

  /**
   * The purpose of this method is to debug the server. It will echo the received file back to the client.
   * 
   * The server will expect the client to send a byte.
   * 
   * 0x00: The server will receive a string, and will echo it back to the client in uppercase.
   * 0x01: The server will receive a byte, and will echo it back to the client +1.
   * 0x02: The server will send a file to the client.
   * 
   * @throws IOException
   */
  public void debug() throws IOException {
    while (true) {

      System.out.println("Waiting for a connection...");
      // Accept the incoming connection 
      accept();
      System.out.println("Connection accepted...");

      // Receive 1 byte from the client
      byte opt = recvBytes(1)[0];

      System.out.println("Option received: " + opt);

      // Do something with the data received (0=String, 1=byte, 2=File)
      if (opt == 0) {
        // Echo the received string
        String str = recvStringLine();
        serverOperation(str);
      } else if (opt == 1) {
        // Echo the received byte
        byte data[] = recvBytes(1);
        serverOperation(data);
      } else if (opt == 2) {
        // Send a file to the client
        sendFile("resources/file.txt");
      }

      System.out.println("Operation done.");
      // Close the connection with the client
      closeClient();

    }
  }

  // ==========================================[ GETTERS AND SETTERS ]==================================================
  public int getPort() {
    return port;
  }

  public void setPort(int port) {
    this.port = port;
  }

}

Cliente

package src;

import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;

/**
 * TCP_Client
 */
public class TCP_Client {
  private String ip;
  private int port;

  Socket socket;
  InputStream recvPipe;
  OutputStream sendPipe;

  /**
   * Constructor for the 
   * 
   * @param ip The IP address of the server
   * @param port The port of the server
   * @throws IOException
   */
  public TCP_Client(String ip, int port) throws IOException {
    this.ip = ip;
    this.port = port;

    socket = new Socket(ip, port);
    recvPipe = socket.getInputStream();
    sendPipe = socket.getOutputStream();
  }

  /*
   * Close the socket
   */
  public void close() throws IOException {
    socket.close();
  }

  // =================================================[ Send and receive methods ]=================================================

  // ------------------------------------- Bytes messages -------------------------------------

  /**
   * Send an array of bytes to the server
   * 
   * @param data The data to send
   * @throws IOException
   */
  public void sendBytes(byte data[]) throws IOException {
    // Send the data
    sendPipe.write(data);
  }

  /**
   * Read all available bytes from the server. The response wont be processed until the server closes the current client socket.
   * 
   * Use the getBlockSize() method and the setBlockSize() method to change the block size
   * 
   * @return An array of bytes with the data received. If the server didn't send any data, returns null
   * @throws IOException
   */
  public byte[] recvBytes() throws IOException {
    // Create an input buffer of dynamic size
    ArrayList<Byte> buffer = new ArrayList<Byte>();
    // Read the data and store them in the buffer (returns the number of bytes read)
    int byteRead = recvPipe.read();

    // If the server didn't send any data, return null
    if (byteRead == -1) {
      return null;
    }

    // Read the data until the server doesn't send any data
    while (byteRead != -1) {
      // Add the data to the buffer
      buffer.add((byte) byteRead);
      // Read the next byte
      byteRead = recvPipe.read();
    }

    // Copy the data from the buffer to a new array of the exact size
    byte data[] = new byte[buffer.size()];
    for (int i = 0; i < buffer.size(); i++) {
      data[i] = buffer.get(i);
    }

    // Return the data
    return data;
  }

  /**
   * Read an specific number of bytes from the server. Wait until the server sends the data
   * 
   * @param size The number of bytes to read
   * @throws IOException
   */
  public byte[] recvBytes(int size) throws IOException {
    // Buffer to store the data
    byte buffer[] = new byte[size];
    // Read the data and store them in the buffer (returns the number of bytes read)

    int totalBytesRead = 0;

    // Read the data until the total number of bytes read is equal to the size
    while (totalBytesRead < size) {
      // Read the data and store them in the buffer (returns the number of bytes read)
      int bytesRead = recvPipe.read(buffer);

      // If the server didn't send any data, try again
      if (bytesRead == -1) {
        continue;
      }

      // Dump the data read on this loop to the buffer
      for (int i = 0; i < bytesRead; i++) {
        buffer[totalBytesRead + i] = buffer[i];
      }

      // Update the total number of bytes read
      totalBytesRead += bytesRead;
    }

    // Copy the data from the buffer to a new array of the exact size
    return buffer;
  }

  // ------------------------------------- String messages -------------------------------------

  /**
  * Send a message to the server
  * 
  * @param msg The message to send
  * @throws IOException
  */
  public void sendString(String msg) throws IOException {
    // Prepare the data to send
    byte data[] = msg.getBytes();
    // Send the data
    sendPipe.write(data);
  }

  /**
   * Read a line from the server. The response wont be processed until the server closes the current client socket.
   * 
   * @return The line received from the server
   * @throws IOException
   */
  public String recvStringLine() throws IOException {
    InputStreamReader in = new InputStreamReader(socket.getInputStream());
    BufferedReader buff = new BufferedReader(in);

    // Read the data and store them in the buffer (returns the number of bytes read)
    String line = buff.readLine();

    return line;
  }

  /**
   * Read a file from the server. The response wont be processed until the server closes the current client socket.
   * 
   * @param filename
   * @throws IOException
   */
  public void recvFile(String filename) throws IOException {
    // Prepare the file to write the response
    FileOutputStream file = new FileOutputStream(filename);

    // Read the response
    byte data[] = recvBytes();
    file.write(data);

    // Close the file
    file.close();
  }

  // =================================================[ Getters and setters ]=================================================
  public String getIp() {
    return ip;
  }

  public void setIp(String ip) {
    this.ip = ip;
  }

  public int getPort() {
    return port;
  }

  public void setPort(int port) {
    this.port = port;
  }

}

UDP

ℹ️
Estos códigos son un ejemplo de uso para establecer una conexión UDP

Servidor

class UDPServidor {
 public static void main(String args[]) throws Exception {
	 DatagramSocket socket = new DatagramSocket(1234);
	 byte[] inBuffer = new byte[1024];

	 while(true) {
		 //Recibe el datagrama
		 DatagramPacket inPack = new DatagramPacket(inBuffer, inBuffer.length);
		 socket.receive(inPack);
		 //Transforma los datos
		 String frase = new String(inPack.getData());
		 String modifiedSentence = sentence.toUpperCase();
		 byte[] outBuffer = modifiedSentence.getBytes();
		 //Envía el datagrama a la dirección que llegó con el paquete
		 InetAddress dirIP = inPack.getAddress();
		 int port = inPack.getPort();
		 DatagramPacket outPack = new DatagramPacket(outBuffer,outBuffer.length,dirIP,port);
		 socket.send(outPack);
	 }
 }
}

Cliente

class UDPCliente {
 public static void main(String args[]) throws Exception {
	 //Creamos datagrama de salida con el mensaje
	 InetAddress dirIP = InetAddress.getByName("192.156.21.21");
	 String sentence = "Mensaje que se desea enviar";
	 byte[] outBuff = sentence.getBytes();
	 DatagramPacket outPac = new DatagramPacket(outBuff,outBuff.length,dirIP,1234);
	 //Creamos el socket y enviamos el datagrama
	 DatagramSocket socket= new DatagramSocket();
	 socket.send(outPac);

	 //Recibimos un datagrama
	 byte[] inputBuff = new byte[1024];
	 DatagramPacket inPac = new DatagramPacket(inputBuff, inputBuff.length);
	 socket.receive(inPac);
	 socket.close();

	 String newSentence = new String(inPac.getData());
	 System.out.println("DEL SERVIDOR:" + newSentence);
 }
}