1. 目录结构

java处理多客户端socket java socket多线程编程_源码

2. StartClient.java源码

package socket.thread;
    import java.net.InetAddress;
    import java.net.UnknownHostException;

    public class StartClient {

     /**
     * 需要先启动服务端StartServer,再运行客户端StartClient
         * 请注意,由于线程运行的不确定性,打印输出语句的次序是不确定的。。。
     * 
     * 但是,不论输出语句的次序如何变化,客户端总有三个线程请求到服务端,
     * 并且服务器端在处理完请求后会关闭Socker和IO。
     * 
     * @param args
     * @throws UnknownHostException
     */
    public static void main(String[] args) throws UnknownHostException {

        int threadNo = 0;
        // 设置连接地址类
        InetAddress addr = InetAddress.getByName("localhost");

        // 从循环中生成3个客户端,每个客户端会开启一个通讯线程,每个线程首先将先向服务器端发送"Hello Server,My id is "的字符串,然后发送”byebye”,终止该线程的通讯。
        for (threadNo = 0; threadNo < 3; threadNo++) {
            new SocketClient(addr);
        }
    }
    }

3. SocketClient.java源码

package socket.thread;
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.net.InetAddress;
    import java.net.Socket;

    public class SocketClient extends Thread {

    // 客户端的socket
    private Socket socket;

    // 线程统计数,用来给线程编号
    private static int cnt = 0;

    // 客户端id
    private int clientId = cnt++;

    // io对象
    private BufferedReader in;

    private PrintWriter out;

    // 构造函数,在创建该实体时执行
    public SocketClient(InetAddress inetAddress) {
        try {
            // 通过inetAddress和端口号初始化Socket对象
            socket = new Socket(inetAddress, 3333);

            // 实例化IO对象
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);

            // 开启线程
            start();
        } catch (IOException e) {
            e.printStackTrace();
            try {
                // 出现异常,关闭socket
                socket.close();
            } catch (IOException e2) {
                e2.printStackTrace();
            }
        }
    }

    // 线程主体方法
    public void run() {
        try {
            // 向Socket信道上传输本客户端的ID号
            out.println("Hello Server,My id is " + clientId);

            // 收到服务端发来的信息,打印出来
            String str = in.readLine();
            System.out.println(str);

            // 向服务器端发送“byebye”结束本次通信
            out.println("byebye");
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            try {
                // 出现异常,关闭socket
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

4. StartServer.java源码

package socket.thread;
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;

        /**
         * 服务器端运行类
         */
        public class StartServer {

    // 服务器监听口号
    static final int portNo = 3333;

    /**
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        // 根据端口号,创建一个ServerSocket类型的服务器端的Socket,用来同客户端通讯。
        ServerSocket serverSocket = new ServerSocket(portNo);
        System.out.println("The Server is start: " + serverSocket);
        try {
            for (;;) {
                // 阻塞,直到有客户端连接
                Socket socket = serverSocket.accept();
                // 通过构造函数,启动线程
                new SocketServer(socket);
            }
        } finally {
            // 关闭socket
            serverSocket.close();
        }
    }
}

5. SocketServer.java源码

package socket.thread;
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.net.Socket;

    /**
     * 使用套接字连接多个客户机 
     * 如果我们在通过程序里引入多线程的机制,可让一个服务器端同时监听并接收多个客户端的请求,并同步地为它们提供通讯服务。
     * 基于多线程的通讯方式,将大大地提高服务器端的利用效率,并能使服务器端能具备完善的服务功能。
     *
     * 这个类通过继承Thread类来实现线程的功能,也就是说,在其中的run方法里,定义了该线程启动后要执行的业务动作。
     */
    public class SocketServer extends Thread {

    // 客户端的socket
    private Socket clientSocket;

    // IO句柄
    private BufferedReader in;

    private PrintWriter out;

    // 默认的构造函数
    public SocketServer() {

    }

    // 带参数构造函数,初始化了本类里的Socket对象,同时实例化了两类IO对象。
    public SocketServer(Socket socket) throws IOException {
        clientSocket = socket;

        // 初始化in和out句柄
        in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())), true);

        // 通过start方法,启动定义在run方法内的本线程的业务逻辑。
        start();

    }

    // 线程执行的主体内容
    public void run() {
        try {
            // 用循环来监听通讯内容。for(;;) 等同于while(true),死循环
            for (;;) {
                // 读取从Socket信道上传输过来的客户端发送的通讯信息。
                String str = in.readLine();
                // 如果得到的信息为“byebye”,则表明本次通讯结束,退出for循环。
                if (str.equals("byebye")) {
                    break;
                }
                // 将字符串发送给客户端
                System.out.println("In Server reveived the info: " + str);
                out.println(str);
            }
            System.out.println("closing the server socket!");
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            System.out.println("close the Server socket and the io.");

            // 关闭客户端的Socket句柄。
            try {
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
    }

    }