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

版权声明:本文为博主原创文章,转载请说明出处,谢谢。 https://blog.csdn.net/qq_27070443/article/details/61619387

主要内容:

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

服务器转发是什么呢?

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

源码实例:

客户端

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输入流等等

猜你喜欢

转载自blog.csdn.net/qq_27070443/article/details/61619387