05_通信案例

群聊案例

服务端

package login;

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

public class Server {
    public static List<Socket> onlineSockets = new ArrayList<>();  // 用于存储"在线用户"所对应的socket对象

    public static void main(String[] args) throws Exception {
        System.out.println("---服务端启动---");

        // 创建 ServerSocket 的对象,同时为服务端注册端口
        ServerSocket serverSocket = new ServerSocket(8888);

        while (true) {
            // 使用 serverSocket 对象,调用一个 accept 方法,等待客户端的连接请求
            Socket socket = serverSocket.accept();
            onlineSockets.add(socket);  // 用户一上线,就将其 socket 存到"在线集合"里面
            System.out.println(socket.getRemoteSocketAddress() + "连接到了服务端");
            // 使用一个独立的线程,把当前的 socket 对象交给它负责处理
            new ServerReaderThread(socket).start();
        }


    }
}

class ServerReaderThread extends Thread {
    private Socket socket;

    public ServerReaderThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            // 从 socket 通信管道中得到一个字节输入流
            InputStream is = socket.getInputStream();
            // 把原始的字节输入流包装成数据输入流
            DataInputStream dis = new DataInputStream(is);

            while (true) {
                try {
                    // 使用数据输入流读取客户端发送过来的消息
                    String msg = dis.readUTF();
                    // 把消息分发给全部客户端
                    sendMsgtoAll(msg);
                } catch (Exception e) {
                    Server.onlineSockets.remove(socket);  // 用户想要下线,就将其 socket 从 onlineSockets 里面删除
                    System.out.println(socket.getRemoteSocketAddress() + "断开了连接");
                    dis.close();  // 关闭流管道
                    socket.close();  // 关闭连接管道
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void sendMsgtoAll(String msg) throws Exception {
        // 将消息发送给所有在线的 socket 管道
        for (Socket onlineSocket : Server.onlineSockets) {
            OutputStream os = onlineSocket.getOutputStream();
            DataOutputStream dos = new DataOutputStream(os);
            dos.writeUTF(msg);
            dos.flush();  // 注意!这里不能替换为 dos.close(),否则,程序无法运行!
        }

    }
}

客户端

package login;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    public static void main(String[] args) throws Exception {
        // 创建 Socket 对象,并同时请求与服务器程序的连接
        Socket socket = new Socket("127.0.0.1", 8888);

        // 创建独立的线程,负责随机从socket中接收服务器发送过来的消息
        new ClientReaderThread(socket).start();

        // 从 socket 通信管道中得到一个字节输出流,用来发数据给服务端程序
        OutputStream os = socket.getOutputStream();

        // 把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入>>> ");
            String msg = sc.nextLine();

            if ("exit".equals(msg)) {
                System.out.println("欢迎再来!");
                dos.close();  // 关闭数据输出流管道
                socket.close();  // 释放连接资源
                break;
            }
            // 4. 开始写数据出去了
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

class ClientReaderThread extends Thread {
    private Socket socket;

    public ClientReaderThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            // 从 socket 通信管道中得到一个字节输入流,接收来自服务器的数据
            InputStream is = socket.getInputStream();

            // 把低级的字节输入流包装成数据输入流
            DataInputStream dis = new DataInputStream(is);

            while (true) {
                try {
                    // 使用数据输入流读取服务端发送过来的消息
                    String msg = dis.readUTF();
                    System.out.println(msg);
                } catch (Exception e) {
                    System.out.println("服务端断开了连接");
                    dis.close();  // 关闭流管道
                    socket.close();  // 关闭连接管道
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

简易版 BS 架构

BS 架构是不需要客户端的,因为浏览器就是客户端,但是必须要有服务端!

快速实现
// 服务端

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("---服务端启动---");

        // 创建 ServerSocket 的对象,同时为服务端注册端口
        ServerSocket serverSocket = new ServerSocket(80);

        while (true) {
            // 使用 serverSocket 对象,调用一个 accept 方法,等待客户端的连接请求
            Socket socket = serverSocket.accept();

            // 使用一个独立的线程,把当前的 socket 对象交给它负责处理
            new ServerReaderThread(socket).start();
        }


    }
}

class ServerReaderThread extends Thread {
    private Socket socket;

    public ServerReaderThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            // 从 socket 通信管道中得到一个字节输出流
            OutputStream os = socket.getOutputStream();
            // 把原始的字节输出流包装成打印输出流
            PrintStream ps = new PrintStream(os);
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html;charset=UTF-8");
            ps.println();  // 必须换行
            ps.println("<div style='color:red;font-size:120px;text-align:center'>我们都是追梦人</div>");

            // 由于 HTTP 协议是连接一次的,无记忆响应,响应后请关闭本次连接
            ps.close();  // 关闭打印管道
            socket.close();  // 关闭连接管道
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
优化架构
// 服务端

import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("---服务端启动---");

        // 创建 ServerSocket 的对象,同时为服务端注册端口
        ServerSocket serverSocket = new ServerSocket(80);

        // 创建一个线程池,负责处理通信管道的任务
        ThreadPoolExecutor pool = new ThreadPoolExecutor(16 * 2, 16 * 2, 0, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(8), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        while (true) {
            // 使用 serverSocket 对象,调用一个 accept 方法,等待客户端的连接请求
            Socket socket = serverSocket.accept();

            // 使用一个独立的线程,把当前的ServerReaderRunnable任务对象交给它负责处理
            pool.submit(new ServerReaderRunnable(socket));

            // 在服务器上显示连接对象
            System.out.println(socket.getRemoteSocketAddress() + "访问了服务器 [" + new Date() + "]\n");
        }
    }
}

class ServerReaderRunnable implements Runnable {
    private Socket socket;

    public ServerReaderRunnable(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            // 从 socket 通信管道中得到一个字节输出流
            OutputStream os = socket.getOutputStream();
            // 把原始的字节输出流包装成打印输出流
            PrintStream ps = new PrintStream(os);
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html;charset=UTF-8");
            ps.println();  // 必须换行
            ps.println("<div style='color:red;font-size:120px;text-align:center'>我们都是追梦人</div>");

            // 由于 HTTP 协议是连接一次的,无记忆响应,响应后请关闭本次连接
            ps.close();  // 关闭打印管道
            socket.close();  // 关闭连接管道
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值