Java利用TCP编程实现简单聊天室

Posted DLKKILL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java利用TCP编程实现简单聊天室相关的知识,希望对你有一定的参考价值。

前言:

本文是我在学习尚学堂JAVA300集第二季网络编程部分仿照视频内容实现而成
具体可以去尚学堂官网观看视频学习

一、实现思路

   实现聊天室的最核心部分就是JAVA的TCP网络编程。
  TCP 传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议 ,在Java中我们利用ServerSocket类来建立服务端,利用Socket类来建立客户端。这里要注意,在TCP中,Socket实际上是指
Server端与Client端建立的一个双向的流通道,我们利用这个流通道实现数据的传输。
  我们将聊天室分为两部分,客户端和服务端.
  对于客户端,主要有两个功能,信息的收与信息的发。因为这两个功能需要并行进行,并且要不停的进行收和发,所以将这两个功能抽象成两个实现Runnable接口的(Send,Recevice)类,每次客户端Client启动,建立一个Socket,并利用这个Socket建立一个收线程(Recevice类)和发线程(Send)类

  对于服务器端,因为我们要不停的监听是否有新的连接进来,所有要通过一个循环不停的接收,这里的接收函数是阻塞式的。因为我们要对所有的连接进行同时处理,所有我们将新得到连接抽象成一个实现Runnable接口的User类,利用多线程进程对每一个连接并行处理。为了方便多个连接之间的交互,我们将User类作为Server类的一个内部类使用。

二、实现过程

1.客户端

Client类:

package top.dlkkill.tcp.chat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

public class Client {
    
    public static void main(String[] args) throws IOException {
        //从控制台获得输入
        BufferedReader console=new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请输入您的名字:");
        String name=console.readLine();
        if(name.equals(""))
            return;
        Socket client=null;
        try {
            //建立新连接,注意这里创建好就已经连接上了,要保证服务端已经开启
            client=new Socket("localhost", 8888);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            //e.printStackTrace();
            System.err.println(name+"连接失败");
        }
        //两条线路,一条负责发,一条负责收
        new Thread(new Send(client,name)).start();
        new Thread(new Recevice(client)).start();
    }
    
}

Send类

package top.dlkkill.tcp.chat;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

public class Send implements Runnable{
    
    //负责写出,将信息传输到服务端
    private DataOutputStream os;
    //负责读取控制台输入
    private BufferedReader console;
    //线程标识
    private boolean isRun=true;
    
    //通过死循环保证线程一直进行
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(isRun) {
            send(getMsgFromConsole());
        }
    }
    
    //构造方法,利用Socket类获得流
    public Send(Socket client,String name) {
        // TODO Auto-generated constructor stub
        try {
            os=new DataOutputStream(client.getOutputStream());
            console=new BufferedReader(new InputStreamReader(System.in));
            send(name);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            isRun=false;
            CloseUtil.closeAll(os,console);
        }
    }
    
    //发送函数
    public void send(String msg) {
        
        try {
            if(msg!=null&&!msg.equals("")) {
                os.writeUTF(msg);
                os.flush();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            isRun=false;
            CloseUtil.closeAll(os,console);
        }
    }
    
    //从控制台不断读取信息
    public String getMsgFromConsole() {
        String msg=null;
        try {
            msg=console.readLine();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            isRun=false;
            CloseUtil.closeAll(os,console);
        }
        return msg;
    }
}

Recevice类

package top.dlkkill.tcp.chat;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;

public class Recevice implements Runnable{
    
        //负责读取服务端发送过来的信息
        private DataInputStream is;
        //线程标识
        private boolean isRun=true;
        
        
        @Override
        public void run() {
            // TODO Auto-generated method stub
            while(isRun){
                recevice();
            }
        }
        
        public Recevice(Socket client) {
            // TODO Auto-generated constructor stub
            try {
                is=new DataInputStream(client.getInputStream());
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                CloseUtil.closeAll(is);
                isRun=false;
            }
        }
        
        public void recevice() {
            String msg=null;
            try {
                msg=is.readUTF();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                CloseUtil.closeAll(is);
                isRun=false;
            }
            System.out.println(msg);
        }
        
}

2.服务端

Server类(内部有一个User类)

package top.dlkkill.tcp.chat;


import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;

public class Server {
    
    //保存所有的连接
    private HashSet<User> users;
    //线程标识
    private boolean run=true;
    
    public static void main(String[] args) {

        //创建一个Server类
        Server server=new Server();
        try {
            //启动服务器
            server.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public Server() {
        run=true;
        users=new HashSet<User>();
    }
    
    public void start() throws IOException {
        //创建一个服务器端
        ServerSocket server=new ServerSocket(8888);
        while(run) {
            //不断接收一个新的连接,利用新连接创建一个User线程进行处理
            Socket client= server.accept();
            User user=new User(client);
            users.add(user);
            new Thread(user).start();
        }
    }
    
    public void stop() {
        run=false;
    }
    
    
    //代表一个连接,负责信息的接收与转发
    private class User implements Runnable{
        
        //记录连接用户的名字
        private String name;
        
        public String getName() {
            return name;
        }
        //负责接收
        private DataInputStream is;
        //负责发送
        private DataOutputStream os;
        //线程标识
        private boolean isRun=true;
        
        public User(Socket client) {

            try {
                is=new DataInputStream(client.getInputStream());
                os=new DataOutputStream(client.getOutputStream());
                isRun=true;
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                isRun=false;
                CloseUtil.closeAll(is,os);
            }
            try {
                 name=is.readUTF();
                 this.sendOther(new String("欢迎"+name+"进入聊天室"),true);
                 this.send(new String("系统:您已经进入了聊天室"));
            }catch (Exception e) {
                // TODO: handle exception
            }
        }
        
        @Override
        public void run() {
            // TODO Auto-generated method stub
            while(isRun) {
                this.sendOther(this.revice(),false);
            }
        }
        
        //接收信息
        public String revice() {
            String msg = null;
            try {
                msg=is.readUTF();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return msg;
        }
        
        //发送信息
        public void send(String msg) {
            try {
                os.writeUTF(msg);
                os.flush();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        //将信息转发给其他用户,同时实现了私聊功能和系统信息功能
        //因为是内部类,所以可以访问Server类中的private HashSet<User> users
        //@XX:代表向XX发送私聊信息
        public void sendOther(String msg,boolean admin) {
            if(msg.startsWith("@")&&msg.contains(":")) {
                String toname=msg.substring(1, msg.indexOf(":"));
                String newmsg=msg.substring(msg.indexOf(":")+1);
                for (User user : users) {
                    if(user.getName().equals(toname)) {
                        user.send(this.name+"悄悄的对你说:"+newmsg);
                    }
                }
            }else {
                for (User client : users) {
                    if(client!=this) {
                        if(admin)
                            client.send("系统:"+":"+msg);
                        else
                            client.send(this.name+":"+msg);
                    }
                }
            }
        }
    }
}

3.工具类

CloseUtil类(负责关闭流)

package top.dlkkill.tcp.chat;

import java.io.Closeable;

public class CloseUtil {
    public static void closeAll(Closeable ...io) {
        for (Closeable closeable : io) {
            try {
                if(closeable!=null)
                    closeable.close();
            }catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
        }
    }
}

以上是关于Java利用TCP编程实现简单聊天室的主要内容,如果未能解决你的问题,请参考以下文章

Java网络编程基础— 基于TCP的NIO简单聊天系统

Java编程实例-tcp聊天室代码实现

Java网络编程基础---基于TCP的简单聊天系统

网络编程之java简易聊天室实现

java学习笔记之TCP实现的简单聊天

java 网络编程中 tcp连接问题。 例如编写聊天室 , c/s结构的c和s端都是不间断实现请求--响应 。