Java——实验5:java网络编程

实验5:  java网络编程

1. 所使用的工具软件及环境

        环境:Windows 8.1;jdk 1.8;

        工具:eclipse

2. 实验目的

        在Eclipse下编辑、编译、运行、调试简单的Java程序 。

3. 实验内容

        编写图形界面程序,利用Socket和UCP/TCP编写,客户端和服务器端程序可以进行多次会话。示例:

4. 源程序

1)ChatView类
package com.company;

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

public class ChatView {
    String userName;
    JTextField text;
    JTextArea textArea;
    ClientReadAndPrint.ChatViewListen listener;

    //构造函数
    public ChatView(String userName) {
        this.userName = userName ;
        init();
    }

    //初始化函数
    void init() {
        JFrame jf = new JFrame("客户端");
        jf.setBounds(500,200,400,330);      //设置坐标和大小
        jf.setResizable(false);     //缩放为不能缩放

        JPanel jp = new JPanel();
        JLabel lable = new JLabel("用户:" + userName);
        textArea = new JTextArea("**********登录成功!欢迎来到多人聊天室!*************\n",12, 35);
        textArea.setEditable(false);  //设置为不可修改
        JScrollPane scroll = new JScrollPane(textArea);  //设置滚动面板
        scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);  //显示垂直条
        jp.add(lable);
        jp.add(scroll);

        text = new JTextField(20);
        JButton button = new JButton("发送");
        JButton openFileBtn = new JButton("发送文件");
        jp.add(text);
        jp.add(button);
        jp.add(openFileBtn);

        //设置“打开文件”监听
        openFileBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                showFileOpenDialog(jf);
            }
        });

        //设置“发送”监听
        listener = new ClientReadAndPrint().new ChatViewListen();
        listener.setJTextField(text);   //调用PoliceListen类的方法
        listener.setJTextArea(textArea);
        listener.setChatViewJf(jf);
        text.addActionListener(listener);  //文本框添加监听
        button.addActionListener(listener);  //按钮添加监听

        jf.add(jp);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  //设置右上角关闭图标的作用
        jf.setVisible(true);  //设置可见
    }

    //“打开文件”调用函数
    void showFileOpenDialog(JFrame parent) {
        //创建一个默认的文件选择器
        JFileChooser fileChooser = new JFileChooser();
        //设置默认显示的文件夹
        fileChooser.setCurrentDirectory(new File("C:\\Users\\阮琼露\\Desktop"));
        //设置默认使用的文件过滤器(FileNameExtensionFilter的第一个参数是描述,后面是需要过滤的文件扩展名 可变参数)
        fileChooser.setFileFilter(new FileNameExtensionFilter("(txt)", "txt"));
        //打开文件选择框(线程将被堵塞,直到选择框被关闭)
        int result = fileChooser.showOpenDialog(parent);
        //点击确定
        if(result == JFileChooser.APPROVE_OPTION) {
            //获取路径
            File file = fileChooser.getSelectedFile();
            String path = file.getAbsolutePath();
            ClientFileThread.outFileToServer(path);
        }
    }
}
2)Client类
package com.company;

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

public class Client {
    //主函数登录窗口
    public static void main(String[] args) {
        new Login();
    }
}

//客户端的读和写,以及登录和发送的监听
class ClientReadAndPrint extends Thread{
    static Socket mySocket = null;
    static JTextField textInput;
    static JTextArea textShow;
    static JFrame chatViewJFrame;
    static BufferedReader in = null;
    static PrintWriter out = null;
    static String userName;

    //接收从服务端发送来的消息
    public void run() {
        try {
            in = new BufferedReader(new InputStreamReader(mySocket.getInputStream()));
            while (true) {
                String str = in.readLine();   //获取服务端发送的信息
                String str1 = str.substring(1);
                textShow.append( str1 + '\n');  //添加进客户端的文本区域
                textShow.setCaretPosition(textShow.getDocument().getLength());  //设置滚动条在最下面
            }
        } catch (Exception e) {}
    }

    //登录监听(内部类)
    class LoginListen implements ActionListener {
        JTextField textField;
        JPasswordField pwdField;
        JFrame loginJFrame;
        ChatView chatView = null;

        public void setJTextField(JTextField textField) {
            this.textField = textField;
        }
        public void setJPasswordField(JPasswordField pwdField) {
            this.pwdField = pwdField;
        }
        public void setJFrame(JFrame jFrame) {
            this.loginJFrame = jFrame;
        }

        public void actionPerformed(ActionEvent event) {
            userName = textField.getText();
            String userPwd = String.valueOf(pwdField.getPassword());  //getPassword方法获得char数组
            if(userName.length() >= 1 && userPwd.equals("123")) {     //密码为123并且用户名长度大于等于1
                chatView = new ChatView(userName);                    //新建聊天窗口,设置聊天窗口的用户名(静态)
                //建立和服务器的联系
                try {
                    InetAddress addr = InetAddress.getByName(null);     //获取主机地址
                    mySocket = new Socket(addr,8081);              //客户端套接字
                    loginJFrame.setVisible(false);                      //隐藏登录窗口
                    out = new PrintWriter(mySocket.getOutputStream());  //输出流
                    out.println("用户【" + userName + "】进入聊天室!");    //发送用户名给服务器
                    out.flush();  // 清空缓冲区out中的数据
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //新建普通读写线程并启动
                ClientReadAndPrint readAndPrint = new ClientReadAndPrint();
                readAndPrint.start();
                //新建文件读写线程并启动
                ClientFileThread fileThread = new ClientFileThread(userName, chatViewJFrame, out);
                fileThread.start();
            }
            else {
                JOptionPane.showMessageDialog(loginJFrame, "账号或密码错误,请重新输入!", "提示", JOptionPane.WARNING_MESSAGE);
            }
        }
    }

    //聊天界面监听(内部类)
    class ChatViewListen implements ActionListener{
        public void setJTextField(JTextField text) {
            textInput = text;
        }
        public void setJTextArea(JTextArea textArea) {
            textShow = textArea;
        }
        public void setChatViewJf(JFrame jFrame) {
            chatViewJFrame = jFrame;
            //设置关闭聊天界面的监听
            chatViewJFrame.addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    out.println("用户【" + userName + "】离开聊天室!");
                    out.flush();
                    System.exit(0);
                }
            });
        }

        //监听执行函数
        public void actionPerformed(ActionEvent event) {
            try {
                String str = textInput.getText();
                //文本框内容为空
                if("".equals(str)) {
                    textInput.grabFocus();  //设置焦点(可行)
                    JOptionPane.showMessageDialog(chatViewJFrame, "输入为空,请重新输入!", "提示", JOptionPane.WARNING_MESSAGE);
                    return;
                }
                out.println("用户【" + userName + "】发言:" + str);  //输出给服务端
                out.flush();   //清空缓冲区out中的数据

                textInput.setText("");  //清空文本框
                textInput.grabFocus();
            } catch (Exception e) {}
        }
    }
}
3)ClientFileThread类
package com.company;

import java.io.*;
import java.net.*;
import javax.swing.*;

public class ClientFileThread extends Thread{
    private Socket socket = null;
    private JFrame chatViewJFrame = null;
    static String userName = null;
    static PrintWriter out = null;      //普通消息的发送(Server.java传来的值)
    static DataInputStream fileIn = null;
    static DataOutputStream fileOut = null;
    static DataInputStream fileReader = null;
    static DataOutputStream fileWriter = null;

    public ClientFileThread(String userName, JFrame chatViewJFrame, PrintWriter out) {
        ClientFileThread.userName = userName;
        this.chatViewJFrame = chatViewJFrame;
        ClientFileThread.out = out;
    }

    //客户端接收文件
    public void run() {
        try {
            InetAddress addr = InetAddress.getByName(null);     //获取主机地址
            socket = new Socket(addr, 8090);  // 客户端套接字
            fileIn = new DataInputStream(socket.getInputStream());  // 输入流
            fileOut = new DataOutputStream(socket.getOutputStream());  // 输出流
            //接收文件
            while(true) {
                String textName = fileIn.readUTF();
                long toLength = fileIn.readLong();
                int result = JOptionPane.showConfirmDialog(chatViewJFrame, "是否接受?", "提示", JOptionPane.YES_NO_OPTION);
                int length = -1;
                byte[] buff = new byte[1024];
                long curLength = 0;
                //选择是否接受文件,0为确定,1位取消
                if(result == 0){
    				out.println("用户【" + userName + "】选择接收文件:"+ textName );
					out.flush();
                    File userFile = new File("C:\\Users\\阮琼露\\Desktop\\接受文件\\" + userName);
                    //新建当前用户的文件夹
                    if(!userFile.exists()) {
                        userFile.mkdir();
                    }
                    File file = new File("C:\\Users\\阮琼露\\Desktop\\接受文件\\" + userName + "\\"+ textName);
                    fileWriter = new DataOutputStream(new FileOutputStream(file));
                    while((length = fileIn.read(buff)) > 0) {
                        //把文件写进本地
                        fileWriter.write(buff, 0, length);
                        fileWriter.flush();
                        curLength += length;
						out.println("【接收进度:" + curLength/toLength*100 + "%】");
						out.flush();
                        if(curLength == toLength) {
                            //强制结束
                            break;
                        }
                    }
                    out.println("用户【" + userName + "】已接受文件:"+ textName );
                    out.flush();
                    //提示文件存放地址
                JOptionPane.showMessageDialog(chatViewJFrame, "文件存放地址:\n" +
                            "C:\\Users\\阮琼露\\Desktop\\接受文件\\" +
                            userName + "\\" + textName, "提示", JOptionPane.INFORMATION_MESSAGE);
                }
                else {
                    //不接受文件
                    out.println("用户【" + userName + "】拒绝接收该文件!");
                    out.flush();
                    while((length = fileIn.read(buff)) > 0) {
                        curLength += length;
                        if(curLength == toLength) {
                            //强制结束
                            break;
                        }
                    }
                }
                fileWriter.close();
            }
        } catch (Exception e) {}
    }

    //客户端发送文件
    static void outFileToServer(String path) {
        try {
            File file = new File(path);
            fileReader = new DataInputStream(new FileInputStream(file));
            fileOut.writeUTF(file.getName());  //发送文件名字
            fileOut.flush();
            fileOut.writeLong(file.length());  //发送文件长度
            fileOut.flush();
            int length = -1;
            byte[] buff = new byte[1024];
            while ((length = fileReader.read(buff)) > 0) {
                //发送内容
                fileOut.write(buff, 0, length);
                fileOut.flush();
            }
            out.println("用户【" + userName + "】已成功发送文件:"+ file.getName());
            out.flush();
        } catch (Exception e) {}
    }
}
4)Login类
package com.company;

import java.awt.*;
import javax.swing.*;

public class Login {
    JTextField textField = null;
    JPasswordField pwdField = null;
    ClientReadAndPrint.LoginListen listener=null;

    // 构造函数
    public Login() {
        init();
    }

    void init() {
        JFrame jf = new JFrame("登录");
        jf.setBounds(500, 250, 310, 210);
        jf.setResizable(false);

        JPanel jp1 = new JPanel();
        JLabel headJLabel = new JLabel("登录界面");
        headJLabel.setFont(new Font(null, 0, 35));
        jp1.add(headJLabel);

        JPanel jp2 = new JPanel();
        JLabel nameJLabel = new JLabel("用户名:");
        textField = new JTextField(20);
        JLabel pwdJLabel = new JLabel("密码:    ");
        pwdField = new JPasswordField(20);
        JButton loginButton = new JButton("登录");
        JButton registerButton = new JButton("注册");  // 没设置功能
        jp2.add(nameJLabel);
        jp2.add(textField);
        jp2.add(pwdJLabel);
        jp2.add(pwdField);
        jp2.add(loginButton);
        jp2.add(registerButton);

        JPanel jp = new JPanel(new BorderLayout());
        jp.add(jp1, BorderLayout.NORTH);
        jp.add(jp2, BorderLayout.CENTER);

        //设置监控
        listener = new ClientReadAndPrint().new LoginListen();  //新建监听类
        listener.setJTextField(textField);      //调用PoliceListen类的方法
        listener.setJPasswordField(pwdField);
        listener.setJFrame(jf);
        pwdField.addActionListener(listener);   //密码框添加监听
        loginButton.addActionListener(listener);  //按钮添加监听

        jf.add(jp);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  //设置关闭图标作用
        jf.setVisible(true);
    }
}
 5)MultiChat类
package com.company;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.*;

public class MultiChat {
    JTextArea textArea;

    //用于向文本区域添加信息
    void setTextArea(String str) {
        textArea.append(str+'\n');
    textArea.setCaretPosition(textArea.getDocument().getLength());  //设置滚动条在最下面
    }

    //构造函数
    public MultiChat() {
        init();
    }

    void init() {
        JFrame jf = new JFrame("服务器端");
        jf.setBounds(500,100,450,500);
        jf.setResizable(false);  // 设置为不可缩放

        JPanel jp = new JPanel();  //新建容器
        JLabel label = new JLabel("欢迎来到多人聊天系统(服务器端)");
        textArea = new JTextArea(23, 38);
        textArea.setEditable(false);
        JScrollPane scroll = new JScrollPane(textArea);
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        jp.add(label);
        jp.add(scroll);

        jf.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        jf.add(jp);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setVisible(true);
    }
}
6)Server类
package com.company;

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


public class Server{
    static ServerSocket server = null;
    static Socket socket = null;
    static List<Socket> list = new ArrayList<Socket>();  //存储客户端

    public static void main(String[] args) {
        MultiChat multiChat = new MultiChat();  //新建聊天系统界面
        try {
            //在服务器端对客户端开启文件传输的线程
            ServerFileThread serverFileThread = new ServerFileThread();
            serverFileThread.start();
            server = new ServerSocket(8081);  //服务器端套接字(只能建立一次)
            //等待连接并开启相应线程
            while (true) {
                socket = server.accept();  //等待连接
                list.add(socket);          //添加当前客户端到列表
                //在服务器端对客户端开启相应的线程
                ServerReadAndPrint readAndPrint = new ServerReadAndPrint(socket, multiChat);
                readAndPrint.start();
            }
        } catch (IOException e1) {
            e1.printStackTrace();  //出现异常则打印出异常的位置
        }
    }
}

//服务器端读写类线程,用于服务器端读取客户端的信息并把信息发送给所有客户端
class ServerReadAndPrint extends Thread{
    Socket nowSocket = null;
    MultiChat multiChat = null;
    BufferedReader in =null;
    PrintWriter out = null;

    //构造函数
    public ServerReadAndPrint(Socket s, MultiChat multiChat) {
        this.multiChat = multiChat;  //获取多人聊天系统界面
        this.nowSocket = s;  //获取当前客户端
    }

    public void run() {
        try {
            in = new BufferedReader(new InputStreamReader(nowSocket.getInputStream()));  // 输入流
            //获取客户端信息并把信息发送给所有客户端
            while (true) {
                String str = in.readLine();
                //发送给所有客户端
                for(Socket socket: Server.list) {
                    out = new PrintWriter(socket.getOutputStream());  // 对每个客户端新建相应的socket套接字
                    if(socket == nowSocket) {
                        //发送给当前客户端
                        out.println("  (你)" + str);
                    }
                    else {
                        //发送给其它客户端
                        out.println(str);
                    }
                    out.flush();
                }
                //调用自定义函数输出到图形界面
                multiChat.setTextArea(str);
            }
        } catch (Exception e) {
            Server.list.remove(nowSocket);  //线程关闭,移除相应套接字
        }
    }
}
7)ServerFileThread类
package com.company;

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

public class ServerFileThread extends Thread{
    ServerSocket server = null;
    Socket socket = null;
    static List<Socket> list = new ArrayList<Socket>();  //存储客户端

    public void run() {
        try {
            server = new ServerSocket(8090);
            while(true) {
                socket = server.accept();
                list.add(socket);
                //开启文件传输线程
                FileReadAndWrite fileReadAndWrite = new FileReadAndWrite(socket);
                fileReadAndWrite.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class FileReadAndWrite extends Thread {
    private Socket nowSocket = null;
    private DataInputStream input = null;
    private DataOutputStream output = null;

    public FileReadAndWrite(Socket socket) {
        this.nowSocket = socket;
    }
    public void run() {
        try {
            input = new DataInputStream(nowSocket.getInputStream());  //输入流
            while (true) {
                //获取文件名字和文件长度
                String textName = input.readUTF();
                long textLength = input.readLong();
                //发送文件名字和文件长度给所有客户端
                for(Socket socket: ServerFileThread.list) {
                    output = new DataOutputStream(socket.getOutputStream());
                    if(socket != nowSocket) {
                        //发送给其它客户端
                        output.writeUTF(textName);
                        output.flush();
                        output.writeLong(textLength);
                        output.flush();
                    }
                }
                //发送文件内容
                int length = -1;
                long curLength = 0;
                byte[] buff = new byte[1024];
                while ((length = input.read(buff)) > 0) {
                    curLength += length;
                    for(Socket socket: ServerFileThread.list) {
                        output = new DataOutputStream(socket.getOutputStream());
                        if(socket != nowSocket) {
                            //发送给其它客户端
                            output.write(buff, 0, length);
                            output.flush();
                        }
                    }
                    if(curLength == textLength) {
                        break;
                    }
                }
            }
        } catch (Exception e) {
            ServerFileThread.list.remove(nowSocket);  //线程关闭,移除相应套接字
        }
    }
}

5. 实验结果

(1)用户登录

(2)聊天互动

(3)文件发送互动

(4)用户离开

6. 实验体会

        本次实验,我通过java的基于TCP的Socket网络编程编写多人聊天系统。系统分成服务器端和客户端两类,其中客户端需进行账号密码的登录,才可进入聊天室,进行多对多对话;服务器端则类似于后台,显示全部用户的登录、登出情况和所有会话记录。

        对于服务器端:①利用java.net.ServerSocket类创建ServerSocket对象,创建绑定到特定端口的服务器套接字;使用ServerSocket对象中的accept方法等待接受客户端的连接请求,当接收到一个连接请求时,创建一个用于通信的Socket对象;③通过Socket对象的getInputStream()方法获取套接字的输入流(客户端发送的数据),通过getOutputStream()获取网络字节输出流;④使用网络字节输入流InputStream对象中的方法read,读取客户端发送的数据;使用网络字节输出流OutputStream对象中的方法write,给客户端写回数据;⑤最后当客户端离开时,关闭所有流对象。

        对于客户端:利用java.net.Socket类创建Socket对象,每一个客户端使用一个Socket对象表示。然后,与服务器端类似,创建与Socket对象绑定的输入/输出对象六,并利用输入/输出流进行读/写实现与服务器的通信,最后结束通信时关闭所有流对象。

        在实验过程中,我们需要注意:在创建客户端对象Socket时,要去请求服务器,和服务器经过三次握手建立连接网络;并且要让服务器端使用accept方法,明确该时刻请求的客户端对象Socket。

        通过此次实验,我对网络基础知识和基于TCP的Socket网络编程有了一定的了解和掌握。但是,Java的网络功能十分强大,我们学习的还只是皮毛,在后面我将深入地继续去学习!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阮阮的阮阮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值