Java客户端服务端交互

Java客户端服务端交互

  • 客户端
  • 服务端

客户端

import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

/*
* 聊天室客户端
*/
public class Client {
    /*
    * java.net.Socket
    * Socket封装了TCP协议的通讯细节,使用它就可以和远端计算机建立TCP连接,
    * 并基于两条流(一个输入一个输出)与与远端计算机交互数据
    */
    private Socket socket;

    public Client(){
        try {
            System.out.println("连接服务端");
            /*
             * 实例化Socket就是与远端计算机建立连接的过程。
             * 这里参数1:服务端计算机的IP地址,参数2:服务端打开的服务端口
             * 我们通过IP找到服务器计算机通过端口连接到服务器上的服务端应用程序。
             */
            socket = new Socket("localhost",8088);
            System.out.println("已连接服务端");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void start(){
        try {
            //启动用来读取服务端消息的线程
            ServerHandler sh = new ServerHandler();
            Thread thread = new Thread(sh);
            thread.setDaemon(true);     //守护线程
            thread.start();

            OutputStream ops = socket.getOutputStream();
            OutputStreamWriter opsw = new OutputStreamWriter(ops, StandardCharsets.UTF_8);
            BufferedWriter bw = new BufferedWriter(opsw);
            PrintWriter pw = new PrintWriter(bw,true);

            Scanner scanner = new Scanner(System.in);
            while (true){
                String str;
                str = scanner.nextLine();
                if ("exit".equals(str)) {
                    break;
                }
                pw.println(str);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                //关闭socket时会与服务端进行挥手操作
                socket.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

    }

    public static void main(String[] args) {
        Client client = new Client();
        client.start();
    }

    /*
    * 该线程负责读取服务端发送过来的消息
    */
    private class ServerHandler implements Runnable{
        @Override
        public void run() {
            try {
                InputStream is = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
                BufferedReader br = new BufferedReader(isr);

                String message;
                while ((message = br.readLine()) != null){
                    System.out.println(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

}

服务端

多线程

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/*
 * 聊天室服务端
 */
public class Server {
    private ServerSocket serverSocket;
    //该集合存放所有客户端发送的消息
    private List<PrintWriter> allout = new ArrayList<>();

    public Server() {
        try {
            System.out.println("启动服务端");
            /*
             * 创建ServerSocket的同时要申请服务端口,该端口不能与系统其他程序
             * 开启的端口一致,否则会抛出异常:
             *   java.io.BindException:address already in use
             * 解决办法:
             * 更换端口
             *   或
             * 杀死占用该端口的程序进程:实际开发中8088很少被占用,通常都是由于我们
             * 启动了两次服务端导致的(第一次启动已经占用了8088,那么第二次启动时
             * 端口会显示被占用)
             */
            serverSocket = new ServerSocket(8088);
            System.out.println("服务端启动成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void start() {
        try {
            while (true) {
                System.out.println("等待客户端连接");
                /*
                 * ServerSocket提供的一个重要方法:
                 *    Socket accept()
                 * 该方法是一个阻塞方法,调用会程序会卡住,直到一个客户端与服务端建立连接
                 * 为止,此时返回的Socket就可与连接的客户端进行交互了。
                 */
                Socket socket = serverSocket.accept();
                String host = socket.getInetAddress().getHostAddress();
                System.out.println("客户端" + host + "连接成功");

                /*
                 * 启用一个线程处理与该客户端的交互
                 */
                ClientHanlder ch = new ClientHanlder(socket);
                Thread thread = new Thread(ch);
                thread.start();
            }

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

    public static void main(String[] args) {
        Server server = new Server();
        server.start();
    }

    /*
     * 将处理客户端交互的操作在单独的线程上进行,
     * 这里主要工作就是与某个客户端交互
     */
    private class ClientHanlder implements Runnable {
        private Socket socket;
        private String host;    //记录客户端的IP地址信息

        public ClientHanlder(Socket socket) {
            this.socket = socket;
            /*
             * getInetAddress()获取客户端ip地址
             * getHostAddress()将IP地址转换为String类型
             */
            host = socket.getInetAddress().getHostAddress();
        }

        @Override
        public void run() {
            PrintWriter pw = null;
            /*
             * 通过Socket获取输入流可以读取来自远端计算机发送过来的消息
             */
            try {
                InputStream is = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
                BufferedReader br = new BufferedReader(isr);

                //输出消息给客户端
                OutputStream ops = socket.getOutputStream();
                OutputStreamWriter osw = new OutputStreamWriter(ops, StandardCharsets.UTF_8);
                BufferedWriter bw = new BufferedWriter(osw);
                pw = new PrintWriter(bw, true);

                //将输出流存入共享集合allout
                synchronized (allout) {
                    allout.add(pw);
                }
                System.out.println(host + "上线了,当前在线人数:" + allout.size());

                String message;
                /*
                 * 这里循环读取客户端传递过来的消息可能会出现异常:
                 *   java.net.SocketException: Connection reset
                 * 这个错误是由于远端异常断开导致的(没有进行TCP挥手断开操作),
                 * 该异常无法通过逻辑来避免
                 * */
                while ((message = br.readLine()) != null) {
                    System.out.println(host + ": " + message);
                    //将消息回复给客户端
                    synchronized (allout) {
                        for (PrintWriter p : allout) {
                            p.println(host + ":" + message);
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //处理客户端断开后的操作
                synchronized (allout) {
                    allout.remove(pw);      //将当前客户端输出流从共享集合中删除
                }
                System.out.println(host + "下线了,当前人数:" + allout.size());
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

你可能感兴趣的:(java,java,服务器,网络)