多线程与网络编程——基于TCP/IP的网络聊天室实现

概述

1.服务端通过多线程,实现多人连接。
2.服务端接受来自客户端的消息,通过多线程实现转发给各个客户端,实现多人聊天的功能。
3.客户端将接受到的消息记录实时的写入到本地文件。

技术栈

1.多线程
2.TCP/IP基于Socket的通信
3.数组

项目实现

运行示例:

在这里插入图片描述
由于编译器编码的问题,在windows上出现乱码

存储数据:
在这里插入图片描述

项目结构:

在这里插入图片描述

项目实现:

工具类:

package comm;

import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 工具类(处理数据)
 * @author bxwl
 * @create 2020-07-22 14:07
 */
public class Tools {

    /**
     * 消息提示
     * @param content
     */
    public static void tips(String content){
        System.out.println(content);
    }

    /**
     * 获取客户端ip
     * @param s
     * @return
     */
    public static String getIp(Socket s){
        return s.getInetAddress().getHostAddress()+":";
    }

    /**
     * 获取客户端主机名
     * @param s
     * @return
     */
    public static String getHost(Socket s){
        return s.getInetAddress().getHostName()+":";
    }

    /**
     * 获取当前时间
     * @param t
     * @return
     */
    public static String getNowTime(long t){
        //根据提供的时间毫秒数构建日期对象
        Date d = new Date(t);
        SimpleDateFormat sdf = new SimpleDateFormat("[HH:mm:ss]");
        return sdf.format(d);
    }

    /**
     * 获取当前日期
     * @return
     */
    public static String getDate(){
        Date date = new Date(System.currentTimeMillis());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd");
        return sdf.format(date);
    }

    /**
     * 通过输出流发送文本消息
     * @param os
     * @param msg
     */
    public static void sendMsg(OutputStream os, String msg){
        PrintWriter pw = new PrintWriter(os);
        pw.println(msg);
        pw.flush();
    }
}

服务端:

package server;

import comm.Tools;

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

/**
 * @author bxwl
 * @create 2020-07-22 13:55
 */
public class SQServer {

    /***对外提供服务的端口号*/
    private int port;
    //用于记录所有产生的Socket对象
    public volatile static List<Socket> clients = new ArrayList<>();

    public SQServer(int port) {
        this.port = port;
    }

    public void startServer(){
        try {
            //创建服务
            ServerSocket ss = new ServerSocket(port);
            Tools.tips("服务已开启,等待连接.....");
            while(true){
                //监听客户端连接
                Socket s = ss.accept();
                //将获取的Socket存储到集合中
                clients.add(s);
                Tools.tips("客户端已连接:"+Tools.getIp(s));
                //启动聊天服务线程
                new TalkingServer(s).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args){

        new SQServer(8888).startServer();
    }
}

聊天服务线程:

package server;

import comm.Tools;

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

/**
 * 聊天服务线程
 * @author bxwl
 * @create 2020-07-22 14:05
 */
public class TalkingServer extends Thread{

    private Socket s;

    public TalkingServer(Socket s) {
        this.s = s;
    }

    @Override
    public void run() {
        try {
            //发送欢迎消息给客户端
            Tools.sendMsg(s.getOutputStream(),"欢迎使用SOFTEEM多人聊天工具!");
            //启动消息接受和转发线程
            new MsgServer(s).start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

消息接受和转发线程:

package server;

import comm.Tools;

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

/**
 * 消息接受和转发线程
 * @author bxwl
 * @create 2020-07-22 14:27
 */
public class MsgServer extends Thread{

    private Socket s;

    public MsgServer(Socket s){
        this.s = s;
    }


    @Override
    public void run() {
        //读取消息
        try {
            //获取Socket输入流并包装为缓冲字符流
            BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
            //临时变量用于接受每一次读取的文本信息
            String msg = null;
            while((msg = br.readLine()) != null){
                //遍历每一个Socket对象
                for (Socket c:SQServer.clients) {
                    String content = Tools.getNowTime(System.currentTimeMillis()) + Tools.getHost(s) + msg;
                    Tools.sendMsg(c.getOutputStream(),content);
                }
            }
        } catch (IOException e) {
            Tools.tips("客户端断开连接:"+Tools.getIp(s));
            //从集合中移除该对象
            SQServer.clients.remove(s);
        }
    }
}

客户端:

package client;

import comm.Tools;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

/**
 * 聊天服务客户端
 * @author bxwl
 * @create 2020-07-22 14:57
 */
public class SQClient {

    /**远程服务器的ip地址*/
    private String ip;
    /**远程服务端应用的端口*/
    private int port;

    public SQClient(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }
    
    public void startChat(){
        //连接到指定地址指令端口的服务
        try {
            Socket s = new Socket(ip,port);
            //获取文本扫描对象
            Scanner sc = new Scanner(System.in);
            OutputStream os = s.getOutputStream();
            //启动消息接受线程
            MsgReceiver mr = new MsgReceiver(s);
            mr.setDaemon(true);
            mr.start();
            String msg = "";
            while(!"quit".equals(msg)){
                msg = sc.nextLine();
                Tools.sendMsg(os,msg);
            }
            System.out.println("结束聊天!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        new SQClient("192.168.1.107",8888).startChat();
    }
}

消息接收线程:

package client;

import comm.Tools;

import javax.swing.filechooser.FileSystemView;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

/**
 * 客户端消息接收线程
 * @author bxwl
 * @create 2020-07-22 15:42
 */
public class MsgReceiver extends Thread{

    private Socket s;

    public MsgReceiver(Socket s) {
        this.s = s;
    }

	/**
	*获取存储文件对象
	*/
    public File getPath(){
        //获取与本机有关的文件系统预览
        FileSystemView fsv = FileSystemView.getFileSystemView();
        //获取桌面目录
        File desktop = fsv.getHomeDirectory();
        return desktop;
    }
	
	/**
     * 存储聊天记录
     * @param msg
     * @throws IOException
     */
    public void saveRecord(String msg) throws IOException {
        File f = new File(getPath(),Tools.getDate()+".txt");
        if(!f.exists()){
            f.createNewFile();
        }
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f,true)));
        bw.write(msg+"\r\n");
        bw.flush();
    }

    @Override
    public void run() {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(s.getInputStream()));
            String msg = null;
            while((msg = br.readLine()) != null){
                saveRecord(msg);
                Tools.tips(msg);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值