IM即时通讯(三) 多客户端文本传输之聊天功能

来源:互联网 发布:王中磊和女星 知乎 编辑:程序博客网 时间:2024/05/22 01:41

主要内容:

多线程的应用读写分离思想服务器转发

服务器转发是什么呢?

可以假想服务器是一个大型的数据中心,按照一定的规则,将信息送到你想送去的地方。而且,这里的规则是由开发者自己定义的。

源码实例:

客户端

package me.mxzf;import java.io.IOException;import java.net.InetAddress;import java.net.Socket;import java.net.UnknownHostException;/** *  * @Title: Client * @Dscription: 客户端 * @author Deleter * @date 2017年3月12日 下午2:29:00 * @version 1.0 */public class Client {    public static void main(String[] args) throws UnknownHostException,            IOException {        // 创建Socket实例        Socket socket = new Socket(InetAddress.getLocalHost(), 1234);        // 包装写        new ThreadWriter(socket).start();        // 包装读        new ThreadReader(socket).start();    }}

客户端读线程

package me.mxzf;import java.io.DataInputStream;import java.io.IOException;import java.net.Socket;/** *  * @Title: ThreadReader * @Dscription: 读线程 * @author Deleter * @date 2017年3月12日 下午2:32:46 * @version 1.0 */public class ThreadReader extends Thread {    private Socket socket;    private DataInputStream dis;    private String content;    public ThreadReader(Socket socket) {        this.socket = socket;    }    @Override    public void run() {        try {            // 用DataInputStream包装socket的输入流            dis = new DataInputStream(this.socket.getInputStream());            while (true) {                // 一直阻塞,直到有消息到来,进入循环体                while ((content = dis.readUTF()) != null) {                    // 打印接收到的消息                    System.out.println(content);                }            }        } catch (IOException e) {            e.printStackTrace();        }    }}

客户端写线程

package me.mxzf;import java.io.DataOutputStream;import java.io.IOException;import java.net.Socket;/** *  * @Title: ThreadWriter * @Dscription: 写线程 * @author Deleter * @date 2017年3月12日 下午2:33:25 * @version 1.0 */public class ThreadWriter extends Thread {    private Socket socket;    private DataOutputStream dos;    public ThreadWriter(Socket socket) {        this.socket = socket;    }    @Override    public void run() {        try {            // 用DataOutputStream包装socket的输出流            dos = new DataOutputStream(socket.getOutputStream());            while (true) {                // 等待3后                Thread.sleep(3000);                // 向服务器发送"Hello World!"                dos.writeUTF("Client:Hello World!");                // 刷新缓冲区,防止粘包                dos.flush();            }        } catch (IOException e) {            e.printStackTrace();        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

服务器

package me.mxzf;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import java.util.HashSet;/** *  * @Title: Server * @Dscription: 服务器 * @author Deleter * @date 2017年3月12日 下午2:28:09 * @version 1.0 */public class Server {    // 创建静态的集合来存放"客户通道"    public static HashSet<ServerThread> lists = new HashSet<>();    public static void main(String[] args) throws IOException {        // 创建ServerSocket实例        ServerSocket server = new ServerSocket(1234);        // 在循环的外部定义变量,节约内存空间        Socket socket;        ServerThread serverThread;        while (true) {            // 阻塞直到有客户端接入            socket = server.accept();            // 包装Socket            serverThread = new ServerThread(socket, lists);            serverThread.start();            // 将当前接入的"客户通道"保存在集合中            lists.add(serverThread);        }    }    /**     * 服务器转发     *      * 这种遍历的方式,效率极低,不介意使用     *      * 但是为了初学者更好的理解,我还是用了这种方式     */    public void forword(String serverSocketName, String content) {        try {            // 遍历集合            for (ServerThread serverThread : lists) {                // 如果该ServerThread的toString与即将发送的目的地名称一致时                if (serverThread.toString().equals(serverSocketName)) {                    // 获取该ServerThread的输出流,向其中写出信息                    serverThread.getDataOutputStream().writeUTF(content);                    // 同时,刷新该缓冲区                    serverThread.getDataOutputStream().flush();                }            }        } catch (IOException e) {            e.printStackTrace();        }    }}

服务器读写线程

package me.mxzf;import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.IOException;import java.net.Socket;import java.util.HashSet;/** *  * @Title: ServerThread * @Dscription: 服务器读写线程 * @author Deleter * @date 2017年3月12日 下午2:31:57 * @version 1.0 */public class ServerThread extends Thread {    private Socket socket;    private HashSet<ServerThread> list;    private DataInputStream dis;    private DataOutputStream dos;    public ServerThread(Socket socket, HashSet<ServerThread> list) {        this.socket = socket;        this.list = list;    }    @Override    public void run() {        try {            // 用DataInputStream包装socket的输入流            dis = new DataInputStream(socket.getInputStream());            // 用DataOutputStream包装socket的输出流            dos = new DataOutputStream(socket.getOutputStream());            // 定义局部变量            String content;            while (true) {                // 一直阻塞,直到有消息到来,进入循环体                while ((content = dis.readUTF()) != null) {                    // 打印接收到的消息                    System.out.println(content);                    // 服务器反馈消息                    dos.writeUTF("Server:ok");                    // 刷新缓冲区,防止粘包                    dos.flush();                }            }        } catch (IOException e) {            e.printStackTrace();        }    }    /*     * 重写toString方法     *      * @see java.lang.Thread#toString()     */    @Override    public String toString() {        return "ServerThread [socket=" + socket + "]";    }    /*     * 获取输出流     */    public DataOutputStream getDataOutputStream() {        return dos;    }}

注意

1、客户端先进行写操作(重要)2、必须先启动服务端

扩展学习

可以自行扩展,添加自定义的信息格式,或者加入Scanner输入流等等
1 0
原创粉丝点击