JAVA网络聊天室课程设计(socket+swing)

实现功能

1.多客户端模式下,实现客户与客户的单独通信,信息通过服务器中转
2.端到端的通信,实现并行通信模式
3.实现端到端的文件传输
4.图形用户界面

提示:
1 文件保存地址可能需自行创建或者修改到已有目录
2 还没两台机器跑过,需要修改ip地址和端口

测试图样

哎呀别的自己测好了,好麻烦

客户端

import java.net.*;
import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Client {
    private final int port=4396;
    private final String ipAddress="127.0.0.1";//调试时默认主机ip地址
    private Socket client=null;
    private Poster po;
    private Chaser ch;
    private UI ui;

    private void Sender(){
        Thread s = new Thread(po);
        s.start();
        //检测程序结束运行后向发送关闭指令
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            public void run() {
                System.out.println("Closed");
                try{
                    po.sendMsg("bye");
                }catch(IOException d){
                    System.out.println(d);
                }
            }
        }));
    }
    private void Receiver(){
        Thread r = new Thread(ch);
        r.start();
    }
    public Client()throws Exception{
        //创建socket
        client = new Socket(ipAddress, port);//服务端地址和端口
        System.out.println("Connecting server...");
        BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
        po = new Poster(client,sin);
        ui = new UI(po);
        //注册窗口
        String name = null;
        while(name==null){
            name = JOptionPane.showInputDialog(ui,"请输入用户名","输入用户名",1);
        }
        po.sendMsg(name);
        JOptionPane.showMessageDialog(ui,"欢迎来到孤岛, " + name,"注册成功",JOptionPane.PLAIN_MESSAGE);
        ui.setTitle("岛民  " + name);
        po.sendMsg("alive");//初始化在线名单

        ch = new Chaser(client,ui);
    }
    public static void main(String[] args){
        try {
            Client c = new Client();
            c.Sender();
            c.Receiver();
        } catch(Exception e) {
            System.out.println(e);
        }
    }
}

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;

class UI extends JFrame {
    private JTextArea j1 = new JTextArea("", 10, 20);//文本显示区域
    private JTextArea j2 = new JTextArea("", 5, 9);//输入消息区域
    private JComboBox cmb = new JComboBox();;//在线成员下拉列表
    private JButton b1 = new JButton("发送");
    private JButton b2 = new JButton("文件");
    private Poster po;

    public void ShowMsg(String msg){
        j1.append(msg + "\n");
        j1.setCaretPosition(j1.getText().length());
    }

    public void UpdateAlivers(String name,boolean alive){
        if(alive){
            cmb.addItem(name);
        }else{
            cmb.removeItem(name);
        }
    }

    public UI(Poster po) {
        this.po = po;

        //显示文本域初始化
        JScrollPane jsp1 = new JScrollPane(j1);//将文本域放入滚动窗口
        j1.setBackground(Color.decode("#E1E1E1"));
        j1.setEditable(false);
        Dimension size1 = j1.getPreferredSize();    //获得文本域的首选大小
        jsp1.setBounds(0, 0, size1.width, size1.height);

        //成员列表初始化
        cmb.addItem("所有人");

        //输出文本域初始化
        JScrollPane jsp2 = new JScrollPane(j2);
        j2.setBackground(Color.decode("#E1E1E1"));
        Dimension size2 = j2.getPreferredSize();    //获得文本域的首选大小
        jsp2.setBounds(0, 0, size2.width, size2.height);

        //发送按钮事件监听
        b1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                String msg = j2.getText();
                String targ = cmb.getSelectedItem().toString();
                j2.setText("");//清空输出框
                j1.append("[" + targ + "] 我: " + msg + "\n");
                j1.setCaretPosition(j1.getText().length());
                try {
                    po.sendMsg("to");
                    po.sendMsg(targ);
                    po.sendMsg(msg);
                }catch(IOException a){
                    System.out.println(a);
                }
            }
        });

        //文件按钮事件监听
        b2.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                JFileChooser fc = new JFileChooser("F:\\");
                int val = fc.showOpenDialog(null);    //文件打开对话框
                if (val == fc.APPROVE_OPTION) {
                    try{
                        File F = fc.getSelectedFile();
                        String targ = cmb.getSelectedItem().toString();
                        if(targ.equals("所有人")){
                            int tot = cmb.getItemCount();
                            for(int i=1;i<=tot-1;i++){
                                po.sendMsg("file");
                                po.sendMsg(cmb.getItemAt(i).toString());
                                po.sendMsg(F.getName());
                                po.sendFile(F.toString());
                            }
                        }else{
                            po.sendMsg("file");
                            po.sendMsg(targ);
                            po.sendMsg(F.getName());
                            po.sendFile(F.toString());
                        }
                    }catch(IOException e){
                        System.out.println(e);
                    }
                }
            }
        });

        //盒式布局
        Box boxv = Box.createVerticalBox();
            boxv.add(jsp1);
            boxv.add(cmb);
            Box boxh = Box.createHorizontalBox();
                Box boxv2 = Box.createVerticalBox();
                boxv2.add(b1);
                boxv2.add(b2);
            boxh.add(jsp2);
            boxh.add(boxv2);
        boxv.add(boxh);

        //设置窗口属性
        setContentPane(boxv);
        setTitle("聊天室");
        setSize(400, 300);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setVisible(true);
    }
}
import java.io.*;
import java.net.*;
/*
Poster类实现Runnable接口,用于并行向服务器发送数据
 */
public class Poster implements Runnable {
    private Socket socket = null;
    private DataOutputStream dos = null;
    private BufferedReader sin = null;

    public void sendMsg(String msg)throws IOException{
        dos.writeUTF(msg);
        dos.flush();
    }

    public void sendFile(String path) throws IOException{
        int len = 512;
        try {
            File f = new File(path);
            FileInputStream fis = new FileInputStream(f);//读文件对象

            byte buffer[] = new byte[512];//读取输入流 ,一次读取512字节
            while (len >= 512) {
                len = fis.read(buffer, 0, 512);
                dos.write(buffer, 0, len);
                dos.flush();
            }
            fis.close();
            System.out.println("[文件读取发送成功!]");
        }catch (FileNotFoundException fnfe){
            System.out.println("[文件未找到]");
        }
    }

    public Poster(Socket socket,BufferedReader sin) throws IOException{
        this.socket = socket;
        this.sin = sin;
        this.dos = new DataOutputStream(socket.getOutputStream());
    }

    public void run(){
        try {
            boolean flag = true;
            do {
                //拓展功能区域
            } while(flag);
            dos.close();
        }catch(Exception e){
            System.out.println(e);
        }
    }
}

import javax.swing.*;
import java.io.*;
import java.net.*;
/*
Chaser类实现Runnable接口,用于并行捕捉服务器传来的数据
 */
public class Chaser implements Runnable{
    private Socket socket = null;
    private DataInputStream dis = null;
    private UI ui = null;

    private void getFile() throws Exception{
        String fileName = dis.readUTF();

        String path = "D:/AhatDownload/" + fileName;//文件名为原文件名,自己指定目录
        File file = new File(path);
        if(file.exists()){
            path += "#";
        }
        file.createNewFile();
        System.out.println(path+"已新建!");

        FileOutputStream fos = new FileOutputStream(path);//文件写入对象
        int n=512,len=512;
        byte[] data = new byte[n];//一次读取字节
        while(len>=n)//判断是否有可以读取的数据
        {
            len=dis.read(data,0,n);
            //一定要用DataInputStream,他有方法可以判断缓冲区是否有内容,这样就不会在没有内容时用read()阻塞了
            fos.write(data,0,len);
            fos.flush();
        }
        System.out.println("[文件下载成功!]");
        JOptionPane.showMessageDialog(ui,"成功接收文件: " + fileName,"文件已接收",JOptionPane.PLAIN_MESSAGE);//弹窗
        fos.close();
    }

    public Chaser(Socket socket,UI ui) throws IOException{
        this.socket = socket;
        this.ui = ui;
        this.dis = new DataInputStream(socket.getInputStream());
    }
    public void run(){
        try{
            boolean flag = true;
            do{
                String op = dis.readUTF();//先读取操作
                if(op.equals("msg")){
                    String msg = dis.readUTF();
                    ui.ShowMsg(msg);
                }else if(op.equals("file")){
                    getFile();
                }else if(op.equals("bye")){
                    flag = false;
                }else if(op.equals("online")){
                    ui.UpdateAlivers(dis.readUTF(),true);
                }else if(op.equals("offline")){
                    ui.UpdateAlivers(dis.readUTF(),false);
                }else{
                    System.out.println("[来自服务器的未知指令]"+op);
                }
            }while(flag);
            dis.close();
        }catch(Exception e){
            System.out.println(e);
        }
    }
}

服务器

import java.net.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class Server {
    private int port = 4396;
    private ServerSocket server;
    private Map<String, OutputStream> map = new ConcurrentHashMap<String,OutputStream>();

    public Server() throws Exception{
        server=new ServerSocket(port);
        System.out.println("server on");
    }

    public void loading()throws Exception{
        while(true){
            Socket socket = server.accept();
            //注册名字
            DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
            DataInputStream  dis = new DataInputStream(socket.getInputStream());

            String name = dis.readUTF();

            //广播其他用户该用户已上线
            Set list = map.entrySet();
            for (Object object : list) {
                Map.Entry<String, OutputStream> entry = (Map.Entry<String, OutputStream>) object;
                DataOutputStream ttdos = new DataOutputStream(entry.getValue());
                ttdos.writeUTF("online");
                ttdos.flush();
                ttdos.writeUTF(name);
                ttdos.flush();
            }

            //为用户开启服务线程
            map.put(name,socket.getOutputStream());
            new Thread(new Waiter(socket,map,name)).start();
            System.out.println( "ip " + socket.getInetAddress() + " name: " + name + " is connected");
        }
    }

    public static void main(String[] args){
        try {
            Server ChatRoomServer = new Server();
            ChatRoomServer.loading();
        } catch(Exception e){
            System.out.println(e);
        }
    }
}

import java.io.*;
import java.net.*;
import java.util.*;

//服务项目
class Waiter implements Runnable{
    private Socket socket;
    private String name;
    private Map<String, OutputStream> map;
    private DataInputStream tdis;
    private DataOutputStream tdos;

    private void sendMsgAll(String msg)throws IOException {
        Set list = map.entrySet();
        for (Object object : list) {
            Map.Entry<String, OutputStream> entry = (Map.Entry<String, OutputStream>) object;
            if(!entry.getKey().equals(name)){
                DataOutputStream ttdos = new DataOutputStream(entry.getValue());
                ttdos.writeUTF("msg");
                ttdos.flush();
                ttdos.writeUTF("[公共] " + name + " : " + msg);
                ttdos.flush();
            }
        }
        System.out.println(name+":"+msg);//服务器监听
    }

    private void sendMsgTo(String targ, String msg) throws IOException{
        DataOutputStream ttdos = new DataOutputStream(map.get(targ));
        ttdos.writeUTF("msg");
        ttdos.flush();
        ttdos.writeUTF("[私聊] " + this.name + " : " + msg);
        ttdos.flush();
    }

    private void sendFileTo(String targ, String filename) throws IOException{
        DataOutputStream ttdos = new DataOutputStream(map.get(targ));
        ttdos.writeUTF("file");
        ttdos.flush();

        ttdos.writeUTF(filename);
        ttdos.flush();

        byte data[] = new byte[512];
        int temp = 512,len = 0;
        while(temp>=512) {
            temp=tdis.read(data,0,512);
            len+=temp;
            ttdos.write(data,0,temp);
            ttdos.flush();
        }

        System.out.println("file length: "+len);
    }

    public void initAliver()throws IOException{
        //将在线名单推送给客户端
        Set list = map.entrySet();

        String aliver="";
        for (Object object : list) {
            Map.Entry<String, OutputStream> entry = (Map.Entry<String, OutputStream>) object;
            aliver=entry.getKey();
            if(!aliver.equals(name)){
                tdos.writeUTF("online");
                tdos.flush();
                tdos.writeUTF(aliver);
                tdos.flush();
            }
        }
    }

    public Waiter(Socket s,Map<String,OutputStream> m,String n)throws IOException{
        socket = s;
        map=m;
        name=n;
        tdis = new DataInputStream(socket.getInputStream());
        tdos = new DataOutputStream(socket.getOutputStream());
    }

    public void run() {
        try {
            boolean flag = true;
            do{
                //从客户端接收指令
                String op = tdis.readUTF();
                System.out.println(name + " op: " + op);
                if(op.equals("to")){
                    String tg = tdis.readUTF();
                    if(tg.equals("所有人")){
                        sendMsgAll(tdis.readUTF());
                    }else if(tg.equals(this.name) || !map.containsKey(tg)){
                        System.out.println("[错误] " + name + " 访问的用户(" + tg + ")不存在");
                    }else{
                        sendMsgTo(tg,tdis.readUTF());
                    }
                }else if(op.equals("file")){
                    String tg = tdis.readUTF();
                    String filename = tdis.readUTF();
                    System.out.println(filename);
                    if(tg.equals(this.name) || !map.containsKey(tg)){
                        System.out.println("[错误] " + name + " 访问的用户(" + tg + ")不存在");
                    }else{
                        sendFileTo(tg,filename);
                    }
                }else if(op.equals("bye")){
                    flag = false;
                }else if(op.equals("alive")){
                    initAliver();
                }else{
                    System.out.println("[来自客户端"+this.name+"的未知指令]"+op);
                }
            }while(flag);

            //用户离线后续操作;
            Set list = map.entrySet();
            for (Object object : list) {
                Map.Entry<String, OutputStream> entry = (Map.Entry<String, OutputStream>) object;
                DataOutputStream ttdos = new DataOutputStream(entry.getValue());
                ttdos.writeUTF("offline");
                ttdos.flush();
                ttdos.writeUTF(name);
                ttdos.flush();
            }
            System.out.println("ip " + socket.getInetAddress() + " name: " + name + " is disconnected");
            map.remove(name);

        } catch (IOException e) {
            System.out.println(e);
        }
    }
}
  • 6
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值