套接字将多个客户端编程到一台服务器

Posted

技术标签:

【中文标题】套接字将多个客户端编程到一台服务器【英文标题】:socket programming multiple client to one server 【发布时间】:2012-04-25 06:13:51 【问题描述】:

如何处理多个客户端连接到一台服务器?我有这个 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) 
          
        
      
    
  

和一个嵌入的小程序,其中部分代码像这样,例如

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。

我该如何处理? 套接字实际上如何与多个客户端/一个服务器一起工作?

【问题讨论】:

【参考方案1】:

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

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 选择器,因此您不必为每个客户端创建线程,但这有点复杂。

【讨论】:

nodejs 不会为每个客户端创建线程,你是说 nodejs 使用 NIO 选择器吗? 是否有理由使用\n\r 而不是\r\n 我看到你定义了一个端口号为final int,这是否意味着每个服务器线程都在同一个端口? 那你如何使用JUnit测试来测试呢?【参考方案2】:

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

// 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");

    



【讨论】:

【参考方案3】:

我想问题是你需要为每个连接启动一个单独的线程并在循环中调用serverSocket.accept() 以接受多个连接。

在同一个端口上有多个连接不是问题。

【讨论】:

【参考方案4】:

请参阅 O'Reilly “Java Cookbook”,Ian Darwin - 食谱 17.4 Handling Multiple Clients。

注意accept()不是线程安全的,所以调用被包裹在synchronized中。

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

【讨论】:

【参考方案5】:

这是多个客户端到一个服务器工作正常的代码.. 试试看:)

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,是的。两个客户端都可以在同一台机器上运行。许多客户端可以在单台机器或多台机器上运行。

以上是关于套接字将多个客户端编程到一台服务器的主要内容,如果未能解决你的问题,请参考以下文章

Linux编程设计——套接字

python学习-网络编程

zeromq 模式,用于多客户端从一台服务器推送套接字拉接收

Flash 和 AIR 中的套接字编程......两个客户端,一个服务器。监听两个端口还是一个?

在 C++ 中处理单个服务器和多个客户端

通过 C 中的套接字编程处理多个客户端