聊天室项目

Client:客户端

package socket;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;

/**
 * 聊天室的客户端
 */
public class Client {
    /*
    java.net.Socket 套接字
    Socket封装了TCP协议的通讯细节,使用它可以和远端计算机建立连接
    并且用两个流读写完成与远端计算机的数据交互
     */
    private Socket socket;

    /**
     * 构造方法,用于初始化客户端的
     */

    public Client(){
        try {
            /*
            实例化Socket时需要传入两个参数:
            参数1:服务端的IP地址信息,"localhost"是一种特殊的写法
            表示本机地址
            参数2:服务端打开的端口,数字取值范围1-65535之间

            通过IP我们可以找到网络上的服务端所在计算机,通过端口可以
            连接到该计算机的服务端应用程序
             */
            System.out.println("正在连接服务端");
            socket = new Socket("localhost",8088);    //localhost
            System.out.println("与服务端建立连接");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 客户端开始工作的方法
     */

    public void start(){
        try {
            //启动用于读取服务端发送过来消息的线程
            ServerHandler handler = new ServerHandler();
            Thread t = new Thread(handler);
            t.setDaemon(true);
            t.start();
            /*
            Socket提供的方法
            OutputStream getOutputStream()
            该方法会返回一个字节输出流,通过这个输出流写出的字节
            会发送给远端计算机
             */
            OutputStream out = socket.getOutputStream();
            OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
            BufferedWriter bw = new BufferedWriter(osw);
            PrintWriter pw = new PrintWriter(bw,true);

            Scanner scanner = new Scanner(System.in);
            while (true){
                String line = scanner.nextLine();
                if("exit".equals(line)){
                    break;
                }
                pw.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

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

    /**
     * 该线程负责循环读取服务端发送过来的消息
     */
    private class ServerHandler implements Runnable{
        public void run(){
            try {
                //通过socket获取输入流,读取服务端发送过来的消息
                InputStream is = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(is,"UTF-8");
                BufferedReader br = new BufferedReader(isr);
                String line;
                while ((line = br.readLine()) !=null){
                    System.out.println(line);
                }

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

ServerSocket:服务端

package socket;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

/**
 * 聊天室的服务端
 */
public class Server {
    /*
    运行在服务端的java.net.ServerSocket主要有两个工作:
    1:向系统申请服务端口,客户端就是通过这个端口与服务端建立连接的
    2:监听该服务端,一旦一个客户端连接就会自动创建一个Socket
        相当于"接起一个电话".通过这个Socket与客户端交互

        我们可以把Socket想象成电话,而ServerSocket则是总机
     */
    private ServerSocket server;
  /*
  存所有客户端输出流的数组,供所有ClientHandler转发消息使用
   */
//    private PrintWriter[] allOut = {};
    private Collection<PrintWriter> allOut = new ArrayList<>();

    public Server(){
        try {
            /*
            实例化ServerSocket的同时需要指定服务端口,如果该端口
            已经被其他程序占用时会抛出异常:
            java.net.BindException:Address already in use
             */
            System.out.println("正在启动服务端...");
            server = new ServerSocket(8088);
            System.out.println("服务端启动完毕!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void start(){
        try {
            /*
            ServerSocket提供的方法
            Socket accept()
            这个方法是一个阻塞方法,调用后就"卡住了",直到一个客户端连接
            该方法会立刻返回一个Socket,通过这个Socket就可以
            和刚建立连接的客户端交互了
             */
            while (true) {
                System.out.println("等待客户端连接...");
                Socket socket = server.accept();
                System.out.println("一个客户端连接了");
                //启动一个线程来处理该客户端
                ClientHandler handler = new ClientHandler(socket);
                Thread t = new Thread(handler);
                t.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

    /*
    定义线程任务,该任务与指定的客户端进行互交
    每当一个客户端连接,就会启动一个线程执行这个任务与该客户端互交
     */
    private class ClientHandler implements Runnable{
        private Socket socket;
        private String host;   //当前客户端的地址信息

        public ClientHandler(Socket socket){
            this.socket=socket;
            //通过socket获取远端计算机的IP地址的字符串格式
            host = socket.getInetAddress().getHostAddress();
        }
        public void run(){
            PrintWriter pw = null;
            try{
                InputStream is = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(is, "UTF-8");
                BufferedReader br = new BufferedReader(isr);

                OutputStream os = socket.getOutputStream();
                OutputStreamWriter osw = new OutputStreamWriter(os,"UTF-8");
                BufferedWriter bw = new BufferedWriter(osw);
                pw = new PrintWriter(bw,true);
                //将该输出流存入共享数组allOut
                synchronized (server) {
                    //1对allOut数组扩容
//                    allOut = Arrays.copyOf(allOut, allOut.length + 1);
//                    //将该输出流存入数组最后一个位置
//                    allOut[allOut.length - 1] = pw;

                    allOut.add(pw);
                }
//                System.out.println(host+"上线了当前在线人数:"+allOut.length);
                System.out.println(host+"上线了当前在线人数:"+allOut.size());

                String line;
                while ((line = br.readLine()) != null) {
                    System.out.println(host+"说:" + line);
                    //遍历allOut数组,给所有客户端回复
                    synchronized (server) {
//                        for (int i = 0; i < allOut.length; i++) {
//                            allOut[i].println(host + "说:" + line);
//                        }
                        for (PrintWriter o : allOut){
                            o.println(host+"说"+line);
                        }
                    }
                }
               }catch (IOException e){
            }finally {
                //处理当客户端关闭的操作
                //将当前客户端的输出流从共享数组allOut中删除
                synchronized (server){
//                    for (int i=0;i<allOut.length;i++) {
//                        if (allOut[i] == pw) {
//                            allOut[i] = allOut[allOut.length - 1];
//                            allOut = Arrays.copyOf(allOut, allOut.length - 1);
//                            break;
//                        }
//                    }
                    allOut.remove(pw);
                }
//                System.out.println(host+"下线了,当前在线人数"+allOut.length);
                System.out.println(host+"下线了,当前在线人数"+allOut.size());
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值