套接字编程:多客户端连接到一个服务器

71

你如何处理多个客户端连接到一个服务器?我有这个 LogServer.java 文件。

import javax.net.ssl.*;
import javax.net.*;
import java.io.*;
import java.net.*;

public class LogServer {
  private static final int PORT_NUM = 5000;
  public static void main(String args[]) {
    ServerSocketFactory serverSocketFactory =
      ServerSocketFactory.getDefault();
    ServerSocket serverSocket = null;
    try {
      serverSocket =
        serverSocketFactory.createServerSocket(PORT_NUM);
    } catch (IOException ignored) {
      System.err.println("Unable to create server");
      System.exit(-1);
    }
    System.out.printf("LogServer running on port: %s%n", PORT_NUM);
    while (true) {
      Socket socket = null;
      try {
        socket = serverSocket.accept();
        InputStream is = socket.getInputStream();
        BufferedReader br = new BufferedReader(
          new InputStreamReader(is, "US-ASCII"));
        String line = null;
        while ((line = br.readLine()) != null) {
          System.out.println(line);
        }
      } catch (IOException exception) {
        // Just handle next request.
      } finally {
        if (socket != null) {
          try {
            socket.close();
          } catch (IOException ignored) {
          }
        }
      }
    }
  }
}

和一个嵌入的 applet,其中部分代码如下所示,例如:

import java.io.*;
import java.util.logging.*;

public class LogTest
{
  private static Logger logger = Logger.getAnonymousLogger();

  public static void main(String argv[]) throws IOException
  {
    Handler handler = new SocketHandler("localhost", 5000);
    logger.addHandler(handler);
    logger.log(Level.SEVERE, "Hello, World");
    logger.log(Level.SEVERE, "Welcome Home");
    logger.log(Level.SEVERE, "Hello, World");
    logger.log(Level.SEVERE, "Welcome Home");
  }
}
现在的问题是,如果我在服务器上运行"java LogServer",它将打开应用程序并等待输入流,如果我打开我的网站,它将开始流式传输日志。但是,如果我在另一台计算机/网络上再次打开一个,第二个网站将不会记录流。似乎这是因为第一个网站仍然绑定到端口5000。
我该如何处理? 套接字在多个客户端/一个服务器中实际上是如何工作的?
5个回答

137

对于每个客户端,您需要启动单独的线程。例如:

public class ThreadedEchoServer {

    static final int PORT = 1978;

    public static void main(String args[]) {
        ServerSocket serverSocket = null;
        Socket socket = null;

        try {
            serverSocket = new ServerSocket(PORT);
        } catch (IOException e) {
            e.printStackTrace();

        }
        while (true) {
            try {
                socket = serverSocket.accept();
            } catch (IOException e) {
                System.out.println("I/O error: " + e);
            }
            // new thread for a client
            new EchoThread(socket).start();
        }
    }
}

并且

public class EchoThread extends Thread {
    protected Socket socket;

    public EchoThread(Socket clientSocket) {
        this.socket = clientSocket;
    }

    public void run() {
        InputStream inp = null;
        BufferedReader brinp = null;
        DataOutputStream out = null;
        try {
            inp = socket.getInputStream();
            brinp = new BufferedReader(new InputStreamReader(inp));
            out = new DataOutputStream(socket.getOutputStream());
        } catch (IOException e) {
            return;
        }
        String line;
        while (true) {
            try {
                line = brinp.readLine();
                if ((line == null) || line.equalsIgnoreCase("QUIT")) {
                    socket.close();
                    return;
                } else {
                    out.writeBytes(line + "\n\r");
                    out.flush();
                }
            } catch (IOException e) {
                e.printStackTrace();
                return;
            }
        }
    }
}

你还可以选择更高级的解决方案,使用NIO选择器,这样你就不必为每个客户端创建线程,但这有点更加复杂。


1
nodejs不为每个客户端创建线程,你是说nodejs使用NIO选择器吗? - niceman
使用\n\r而不是\r\n有什么原因吗? - lucidbrot
我看到你将端口号定义为final int,这是否意味着每个服务器线程都在同一个端口上? - Liu Hantao
你如何使用JUnit测试来测试它? - Liu Hantao
@niceman 不同的编程语言有不同的优势和劣势。在Java中,单独的线程仍然是最简单的方法 - 除非你需要处理大量同时连接的客户端(比如一千个),那么你就必须采用更困难的方式。 - user253751

23

这是处理多个客户端的回显服务器...使用线程运行良好

// echo server
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;


public class Server_X_Client {
public static void main(String args[]){


    Socket s=null;
    ServerSocket ss2=null;
    System.out.println("Server Listening......");
    try{
        ss2 = new ServerSocket(4445); // can also use static final PORT_NUM , when defined

    }
    catch(IOException e){
    e.printStackTrace();
    System.out.println("Server error");

    }

    while(true){
        try{
            s= ss2.accept();
            System.out.println("connection Established");
            ServerThread st=new ServerThread(s);
            st.start();

        }

    catch(Exception e){
        e.printStackTrace();
        System.out.println("Connection Error");

    }
    }

}

}

class ServerThread extends Thread{  

    String line=null;
    BufferedReader  is = null;
    PrintWriter os=null;
    Socket s=null;

    public ServerThread(Socket s){
        this.s=s;
    }

    public void run() {
    try{
        is= new BufferedReader(new InputStreamReader(s.getInputStream()));
        os=new PrintWriter(s.getOutputStream());

    }catch(IOException e){
        System.out.println("IO error in server thread");
    }

    try {
        line=is.readLine();
        while(line.compareTo("QUIT")!=0){

            os.println(line);
            os.flush();
            System.out.println("Response to Client  :  "+line);
            line=is.readLine();
        }   
    } catch (IOException e) {

        line=this.getName(); //reused String line for getting thread name
        System.out.println("IO Error/ Client "+line+" terminated abruptly");
    }
    catch(NullPointerException e){
        line=this.getName(); //reused String line for getting thread name
        System.out.println("Client "+line+" Closed");
    }

    finally{    
    try{
        System.out.println("Connection Closing..");
        if (is!=null){
            is.close(); 
            System.out.println(" Socket Input Stream Closed");
        }

        if(os!=null){
            os.close();
            System.out.println("Socket Out Closed");
        }
        if (s!=null){
        s.close();
        System.out.println("Socket Closed");
        }

        }
    catch(IOException ie){
        System.out.println("Socket Close Error");
    }
    }//end finally
    }
}

以下是客户端的代码。只需按需执行此代码,即可创建多个客户端。

// A simple Client Server Protocol .. Client for Echo Server

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;

public class NetworkClient {

public static void main(String args[]) throws IOException{


    InetAddress address=InetAddress.getLocalHost();
    Socket s1=null;
    String line=null;
    BufferedReader br=null;
    BufferedReader is=null;
    PrintWriter os=null;

    try {
        s1=new Socket(address, 4445); // You can use static final constant PORT_NUM
        br= new BufferedReader(new InputStreamReader(System.in));
        is=new BufferedReader(new InputStreamReader(s1.getInputStream()));
        os= new PrintWriter(s1.getOutputStream());
    }
    catch (IOException e){
        e.printStackTrace();
        System.err.print("IO Exception");
    }

    System.out.println("Client Address : "+address);
    System.out.println("Enter Data to echo Server ( Enter QUIT to end):");

    String response=null;
    try{
        line=br.readLine(); 
        while(line.compareTo("QUIT")!=0){
                os.println(line);
                os.flush();
                response=is.readLine();
                System.out.println("Server Response : "+response);
                line=br.readLine();

            }



    }
    catch(IOException e){
        e.printStackTrace();
    System.out.println("Socket read Error");
    }
    finally{

        is.close();os.close();br.close();s1.close();
                System.out.println("Connection Closed");

    }

}
}

17

我猜问题在于您需要为每个连接启动一个单独的线程,并在循环中调用 serverSocket.accept() 来接受多个连接。

拥有多个连接使用相同的端口并不是问题。


5

请参考O'Reilly出版社的《Java Cookbook》, 作者Ian Darwin - 第17.4 处理多个客户端章。

请注意,accept()方法不是线程安全的,因此需要在synchronized中调用该方法。

64: synchronized(servSock) {
65:     clientSocket = servSock.accept();
66: }

1

这是多客户端连接到一个服务器的代码,已经可以很好地运行了... 试一试吧 :)

Server.java:

import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;

class Multi extends Thread{
private Socket s=null;
DataInputStream infromClient;
Multi() throws IOException{


}
Multi(Socket s) throws IOException{
    this.s=s;
    infromClient = new DataInputStream(s.getInputStream());
}
public void run(){  

    String SQL=new String();
    try {
        SQL = infromClient.readUTF();
    } catch (IOException ex) {
        Logger.getLogger(Multi.class.getName()).log(Level.SEVERE, null, ex);
    }
    System.out.println("Query: " + SQL); 
    try {
        System.out.println("Socket Closing");
        s.close();
    } catch (IOException ex) {
        Logger.getLogger(Multi.class.getName()).log(Level.SEVERE, null, ex);
       }
   }  
}
public class Server {

public static void main(String args[]) throws IOException, 
InterruptedException{   

    while(true){
        ServerSocket ss=new ServerSocket(11111);
        System.out.println("Server is Awaiting"); 
        Socket s=ss.accept();
        Multi t=new Multi(s);
        t.start();

        Thread.sleep(2000);
        ss.close();
    }    




    }   
}

Client1.java:

 import java.io.DataOutputStream;
 import java.io.ObjectInputStream;
 import java.net.Socket;


public class client1 {
   public static void main(String[] arg) {
  try {

     Socket socketConnection = new Socket("127.0.0.1", 11111);


     //QUERY PASSING
     DataOutputStream outToServer = new DataOutputStream(socketConnection.getOutputStream());

     String SQL="I  am  client 1";
     outToServer.writeUTF(SQL);


  } catch (Exception e) {System.out.println(e); }
   }
}

Client2.java

import java.io.DataOutputStream; 
import java.net.Socket;


public class client2 {
    public static void main(String[] arg) {
  try {

     Socket socketConnection = new Socket("127.0.0.1", 11111);


     //QUERY PASSING
     DataOutputStream outToServer = new DataOutputStream(socketConnection.getOutputStream());

     String SQL="I am  Client 2";
     outToServer.writeUTF(SQL);


  } catch (Exception e) {System.out.println(e); }
   }
 }

你好Usama,这两个客户端可以在同一台电脑上吗?! - asma
1
@asma,是的。两个客户端都可以在同一台机器上运行。许多客户端可以在单台或多台机器上运行。 - joker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接