简单聊天室(java版)-阿里云开发者社区

开发者社区> 开发与运维> 正文
登录阅读全文

简单聊天室(java版)

简介: 这是本人从其他地方学习到的关于聊天室的一个模本,我从中截取了一部分关于客户端和服务端通信的Socket的内容。希望对大家对socket有个了解,我写的这些代码可以实现两人或多人在多台电脑上实现简单的对话。
 这是本人从其他地方学习到的关于聊天室的一个模本,我从中截取了一部分关于客户端和服务端通信的Socket的内容。希望对大家对socket有个了解,我写的这些代码可以实现两人或多人在多台电脑上实现简单的对话。在运行时要先运行server(服务端),再运行client(客户端)。Windows获取自己电脑的ip需要再DOS(命令窗口)界面输入ipconfig或者再网络和共享中心已连接的网络查看详细信息。具体的代码如下

客户端代码

 package talkRoom;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * 客户端
 * @author ylg
 *
 */
public class Client {
    //客户端用于与服务端通信的socket
    private Socket socket;
    /**
     *初始化客户端相关内容
     */
    public Client(){
        try {
            /**
             * 实例化socket的过程就是连接的过程通常我们要传入两个参数
             * 1:字符串,服务器的IP地址
             * 2:整数,服务器端申请的端口号
             * (serversocket创建时申请的端口号:8088)
             */
            System.out.println("尝试连接");
            //此处的localhost可以改为运行服务端的那台电脑的的ip地址这样就可以连在一起聊天了
            //localhost指的是本机的ip
            socket =new Socket("localhost", 8088);
            System.out.println("连接成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 客户端用于交互的方法
     */
    public void start(){
        try {
            /**
             * 创建一个线程,用于读取服务器发过来的信息
             */
            Runnable hander=new GetMessageFromServerHandler();
            Thread t=new Thread(hander);
            t.start();
            /**
             * 客户端想向服务发送消息,通过socket花去输出流之后写出数据即可
             */
            OutputStream out=socket.getOutputStream();
            /**
             * 向服务器发送字符串,我们可以将字节流转换为缓冲字符流输出PrintWrint
             * 
             */
            OutputStreamWriter osw=new OutputStreamWriter(out,"UTF-8");
            /**
             * 发送一个字符串就应当立即写出,所以要自动行刷新
             */
            PrintWriter pw=new PrintWriter(osw,true);
            /**
             * 创建scanner,将控制台输入的字符串通过pw发送给服务器
             */
            String message=null;
            Scanner scanner=new Scanner(System.in);
            while(true){
                message=scanner.nextLine();
                pw.println(message);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
          Client client=new Client();
          client.start();
    }
    /**
     * 该线程的作用是让客户端可以读取服务器发送过来的信息
     * @author ylg
     *
     */
    class GetMessageFromServerHandler implements Runnable{
        /**
         * 通过socket获取输入流,在转换为缓冲字符输入流
         * 最后通过循环都读取服务端发送的每一行信息
         */

        public void run() {
            try {
                InputStream in=socket.getInputStream();
                InputStreamReader isr=new InputStreamReader(in,"utf-8");
                BufferedReader br=new BufferedReader(isr);
                String message=null;
                while((message=br.readLine())!=null){
                    System.out.println(message);
                }
            } catch (Exception e) {

            }
        }

    }
}

服务端代码(请先运行服务端)


package talkRoom;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 服务端
 * @author ylg
 *
 */
public class Server {
    /**
     * 用于与客户端连接的ServerSOocket
     */
    private ServerSocket server;
    /**
     * 存放所有客户端的输入流,用于广播信息
     */
    private List<PrintWriter> allOut;
    /**
     * 线程池,用于控制服务端线程数量,并重用线程
     */
    private ExecutorService threadPool;
    /**
     * 构造方法,用于初始化服务器相关内容
     * 
     */
    public Server(){
        try {
            //初始化ServerSocket
            /**
             * 初始化时要求我们传入一个整数,这个整数表示端口号,客户端就是
             * 通过这个端口号连接到服务端的
             */
            server=new ServerSocket(8088);
            /**
             * 初始化存放所有客户端输出流的家集合
             */
            allOut =new ArrayList<PrintWriter>();
            //初始化线程池
            threadPool=Executors.newFixedThreadPool(50);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 将给定的输出流存入共享集合中
     * @param out
     */
    private synchronized void addOut(PrintWriter out){
        allOut.add(out);
    }
    /**
     * 从共享集合中删除给定的删除流
     * @param out
     */
    private synchronized void removeOut(PrintWriter out){
        allOut.remove(out);
    }

      //还要遍历方法,并且三个操作集合的方法互斥
     /**
      * 遍历所有的输出流将给定的字符串发送给所有客户端
      * @param message 服务器接收到的消息
      */
    private synchronized void sendMsgToAllClient(String message){
        for(PrintWriter pw:allOut){
            pw.println(message);
        }
    }
    /**
     * 服务端开始工作的方法
     */
    public void start(){
        try {
            /**
             * socket accept()
             * 该方法是一个阻塞方法,用于等待客户端的连接
             * 一旦一个客户端连接上,该方法就会返回与该客户端通信socket
             */
            System.out.println("等待客户端的连接...");
            /**
             * 死循环的目的是一直监听不同客户端的连接
             */
            while(true){
                Socket socket=server.accept();
                System.out.println("一个客户端连接上了...");
                /**
                 * 当一个客户端连接后,启动一个线程,将该客户端的socket传入,
                 * 是该线程与客户端通信
                 */
                Runnable clientHandler=new ClientHandler(socket);
//              Thread t=new Thread(clientHandler);
//              t.start();
                threadPool.execute(clientHandler);
            } 


        } catch (Exception e) {
        }finally {

        }
    }
    public static void main(String[] args) {
        Server server =new Server();
        server.start();
    }
    /**
     * 该线程的作用是与给定的客户端Socket进行通信
     * @author ylg
     *
     */
    class ClientHandler implements Runnable{
        /**
         * 当前线程用于交流的指定客户端的Socket
         */
        private Socket socket;
        /**
         * 创建线程体时将交互的Socket传入
         * @param socket
         */
        public ClientHandler(Socket socket){
            this.socket=socket;
        }
        /**
         * 定义在try外面是因为finally中要引用
         */
        PrintWriter pw=null;
        public void run(){
            try {
                /**
                 * 通过socket获取输出流,用于将信息发送给客户端
                 */
                OutputStream out=socket.getOutputStream();
                OutputStreamWriter osw=new OutputStreamWriter(out, "utf-8");
                pw=new PrintWriter(osw,true);
                /**
                 * 将该客户端的输出流存入共享集合
                 */
                addOut(pw);
                /**
                 * 通过连接上的客户端的socket获取输入流来读取客户端发送过来的信息
                 */
                InputStream in=socket.getInputStream();
                InputStreamReader isr=new InputStreamReader(in,"UTF-8");
                /**
                 * 包装为缓冲流字符输入流,可以按行读取字符串
                 */
                BufferedReader br=new BufferedReader(isr);
                String message=null;
                while((message=br.readLine())!=null){
                    //将当前的发送的消息广播给所有客户端
                    sendMsgToAllClient(message);
                /*  //System.out.println("客户端说: "+message); 
                    //将读取到的信息发送给客户端
                    pw.println(message);*/
                    //在服务端上显示
                    System.out.println(message);
                }
            } catch (Exception e) {
            }finally{
                /**
                 * linux客户端若断开连接,服务端会读取到null
                 * windows的客户端断开连接,服务端会抛出异常
                 * 所以finally是我们最后处理的最佳地点
                 */
                System.out.println("客户端下线");
                /**
                 * 当客户端断开后,将其输出流从共享集合中删除
                 */
                removeOut(pw);
                /**
                 * 输出在线人数
                 */
                System.out.println("当前在线人数"+allOut.size());
                /**
                 * 不同分别关闭输入流与输出流
                 * 关闭socket即可,因为这两个流都是从socket获取的,就好比打电话
                 * 我们最终挂断电话就自然断开了麦克风和听筒一样
                 */
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}





看不懂的可以在下方留言

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章