📜  在Java Socket 编程中引入线程

📅  最后修改于: 2022-05-13 01:54:34.360000             🧑  作者: Mango

在Java Socket 编程中引入线程

先决条件: Java中的套接字编程

本文假设您具备Java中套接字编程的基本知识以及通信中使用的客户端-服务器模型的基本细节。

为什么在网络编程中使用线程?

原因很简单,我们不希望在特定时间只有一个客户端连接到服务器,而是同时连接多个客户端。我们希望我们的架构同时支持多个客户端。出于这个原因,我们必须在服务器端使用线程,以便每当客户端请求到来时,可以分配一个单独的线程来处理每个请求。

让我们举个例子,假设一个日期时间服务器位于一个地方,比如说 X。作为一个通用服务器,它不服务于任何特定的客户端,而是服务于一整套通用客户端。还假设在特定时间,有两个请求到达服务器。使用我们的基本服务器-客户端程序,即使在纳秒内先出现的请求也能够连接到服务器,而另一个请求将被拒绝,因为没有提供同时处理多个请求的机制。为了克服这个问题,我们在网络编程中使用线程。
下面的文章将重点介绍如何创建一个简单的日期时间服务器来同时处理多个客户端请求

快速概览

像往常一样,我们将创建两个Java文件, Server. Java客户端。Java服务器文件包含两个类,即Server (用于创建服务器的公共类)和ClientHandler (用于使用多线程处理任何客户端)。客户端文件只包含一个公共类客户端(用于创建客户端)。下面是这三个类如何相互交互的流程图。

日期时间服务器 1

服务器端编程(Java)

  • 服务器类:服务器端涉及的步骤与Java中的Socket编程一文类似,稍作改动,在获取流和端口号后创建线程对象。
    1. 建立连接:服务器套接字对象被初始化,并且在一个while循环内,一个套接字对象不断地接受传入的连接。
    2. 获取 Streams:从当前请求的 socket 对象中提取 inputstream 对象和 outputstream 对象。
    3. 创建处理程序对象:获取流和端口号后,使用这些参数创建一个新的客户端处理程序对象(上述类)。
    4. 调用 start() 方法:在这个新创建的线程对象上调用 start() 方法。
  • ClientHandler 类:由于我们将为每个请求使用单独的线程,让我们了解扩展线程的 ClientHandler 类的工作和实现。每次请求到来时都会实例化这个类的一个对象。
    1. 首先,这个类扩展了 Thread 以便它的对象具有 Threads 的所有属性。
    2. 其次,该类的构造函数采用三个参数,可以唯一标识任何传入请求,即Socket 、要读取的DataInputStream和要写入的DataOutputStream 。每当我们收到客户端的任何请求时,服务器都会提取其端口号、DataInputStream 对象和 DataOutputStream 对象,并创建该类的新线程对象并在其上调用 start() 方法。
      注意:每个请求总是有一个三元组的套接字、输入流和输出流。这确保了此类的每个对象都写入一个特定的流而不是多个流。
    3. 在该类的run()方法中,它执行三个操作:请求用户指定是否需要时间或日期,从输入流对象中读取答案,并相应地将输出写入输出流对象。
// Java implementation of  Server side
// It contains two classes : Server and ClientHandler
// Save file as Server.java
  
import java.io.*;
import java.text.*;
import java.util.*;
import java.net.*;
  
// Server class
public class Server 
{
    public static void main(String[] args) throws IOException 
    {
        // server is listening on port 5056
        ServerSocket ss = new ServerSocket(5056);
          
        // running infinite loop for getting
        // client request
        while (true) 
        {
            Socket s = null;
              
            try 
            {
                // socket object to receive incoming client requests
                s = ss.accept();
                  
                System.out.println("A new client is connected : " + s);
                  
                // obtaining input and out streams
                DataInputStream dis = new DataInputStream(s.getInputStream());
                DataOutputStream dos = new DataOutputStream(s.getOutputStream());
                  
                System.out.println("Assigning new thread for this client");
  
                // create a new thread object
                Thread t = new ClientHandler(s, dis, dos);
  
                // Invoking the start() method
                t.start();
                  
            }
            catch (Exception e){
                s.close();
                e.printStackTrace();
            }
        }
    }
}
  
// ClientHandler class
class ClientHandler extends Thread 
{
    DateFormat fordate = new SimpleDateFormat("yyyy/MM/dd");
    DateFormat fortime = new SimpleDateFormat("hh:mm:ss");
    final DataInputStream dis;
    final DataOutputStream dos;
    final Socket s;
      
  
    // Constructor
    public ClientHandler(Socket s, DataInputStream dis, DataOutputStream dos) 
    {
        this.s = s;
        this.dis = dis;
        this.dos = dos;
    }
  
    @Override
    public void run() 
    {
        String received;
        String toreturn;
        while (true) 
        {
            try {
  
                // Ask user what he wants
                dos.writeUTF("What do you want?[Date | Time]..\n"+
                            "Type Exit to terminate connection.");
                  
                // receive the answer from client
                received = dis.readUTF();
                  
                if(received.equals("Exit"))
                { 
                    System.out.println("Client " + this.s + " sends exit...");
                    System.out.println("Closing this connection.");
                    this.s.close();
                    System.out.println("Connection closed");
                    break;
                }
                  
                // creating Date object
                Date date = new Date();
                  
                // write on output stream based on the
                // answer from the client
                switch (received) {
                  
                    case "Date" :
                        toreturn = fordate.format(date);
                        dos.writeUTF(toreturn);
                        break;
                          
                    case "Time" :
                        toreturn = fortime.format(date);
                        dos.writeUTF(toreturn);
                        break;
                          
                    default:
                        dos.writeUTF("Invalid input");
                        break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
          
        try
        {
            // closing resources
            this.dis.close();
            this.dos.close();
              
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

输出

A new client is connected : Socket[addr=/127.0.0.1,port=60536,localport=5056]
Assigning new thread for this client
Client Socket[addr=/127.0.0.1,port=60536,localport=5056] sends exit...
Closing this connection.
Connection closed

客户端编程(Java)

客户端编程与一般套接字编程程序类似,具有以下步骤 -

  1. 建立套接字连接
  2. 沟通
// Java implementation for a client
// Save file as Client.java
  
import java.io.*;
import java.net.*;
import java.util.Scanner;
  
// Client class
public class Client 
{
    public static void main(String[] args) throws IOException 
    {
        try
        {
            Scanner scn = new Scanner(System.in);
              
            // getting localhost ip
            InetAddress ip = InetAddress.getByName("localhost");
      
            // establish the connection with server port 5056
            Socket s = new Socket(ip, 5056);
      
            // obtaining input and out streams
            DataInputStream dis = new DataInputStream(s.getInputStream());
            DataOutputStream dos = new DataOutputStream(s.getOutputStream());
      
            // the following loop performs the exchange of
            // information between client and client handler
            while (true) 
            {
                System.out.println(dis.readUTF());
                String tosend = scn.nextLine();
                dos.writeUTF(tosend);
                  
                // If client sends exit,close this connection 
                // and then break from the while loop
                if(tosend.equals("Exit"))
                {
                    System.out.println("Closing this connection : " + s);
                    s.close();
                    System.out.println("Connection closed");
                    break;
                }
                  
                // printing date or time as requested by client
                String received = dis.readUTF();
                System.out.println(received);
            }
              
            // closing resources
            scn.close();
            dis.close();
            dos.close();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

输出 :

What do you want?[Date | Time]..
Type Exit to terminate connection.
Date
2017/06/16
What do you want?[Date | Time]..
Type Exit to terminate connection.
Time
05:35:28
What do you want?[Date | Time]..
Type Exit to terminate connection.
Geeks
Invalid input
What do you want?[Date | Time]..
Type Exit to terminate connection.
Exit
Closing this connection : Socket[addr=localhost/127.0.0.1,port=5056,localport=60536]
Connection closed

这些程序如何协同工作?

  1. 当客户端(例如 client1)发送连接到服务器的请求时,服务器会分配一个新线程来处理此请求。新分配的线程被授予访问流以与客户端通信的权限。
  2. 分配新线程后,服务器通过其 while 循环再次进入接受状态。
  3. 当第二个请求仍在处理中时,服务器会接受此请求并再次分配一个新线程来处理它。这样,即使有些请求正在处理中,也可以处理多个请求。

如何在您的系统上测试上述程序?

将这两个程序保存在同一个包中或任何地方。然后首先运行服务器。 Java后面跟客户端。Java您可以将客户端程序复制到两个三个单独的文件中并单独运行它们,或者如果您有 Eclipse 之类的 IDE,则从同一个程序运行多个实例。上面显示的输出来自单个客户端程序,如果使用多个客户端,将获得类似的结果。下一篇:多线程聊天应用:服务器端编程、客户端编程