一、实验目的
1.掌握Java中GUI程序的编写,包括事件监听机制。
2.掌握Java的网络通信编程,ServerSocket,Socket类的使用。
3.掌握Java中多线程的编程,Thread类,Runnable接口的使用。
4.掌握用面向对象的方法分析和解决复杂问题。
二、实验原理
1.使用面向对象的设计方法设计程序
2.通过继承JFrame类实现图形化界面
3.通过事件监听器实现按钮点击监听与触发事件
4.利用ServerSocket和Socket类实现网络通信
5.利用多线程技术实现同时读入数据与发送数据
三、使用硬件、软件环境
硬件环境:PC计算机一台,配置CPU为Intel(R)Core(TM)i7-8565U CPU @1.80Ghz内存为8.0GB,硬盘256G+1T(机械硬盘),安装系统为Windows10操作系统。
软件环境:IDEA,JDK12.0
四、实验过程
①服务器:
//Author:Sherlock Novitch in Hfut
// -*- codeing=utf-8 -*-
// @time:2020/10/19 15:14
// @File:ServerFrame.java
// @Software:IDEA
// @blog:https://blog.csdn.net/RealCoder
package experiment2;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.*;
public class ServerFrame extends JFrame {
private JTextField text1,text2; //text1为端口号文本栏,text2为消息输入栏
private JTextArea text; //消息显示区
private JButton button1,button2; //button1对应start,button2对应say
private ServerSocket server; //服务器套接字
private Socket sc; //与客户端建立联系的套接字
private DataInputStream in; //客户端到服务器的输入流
private DataOutputStream out; //服务器到客户端的输出流
private ReadThread readThread; //负责读入信息的线程
private myactionlistenr ac; //监听两个按钮的监听器
private ServerFrame(){ //构造方法
setLayout(new FlowLayout()); //窗体容器整体布局设置为FlowLayout
GUIinit(); //调用组件初始化方法
eventinit(); //调用事件监听器初始化方法
setVisible(true); //设置可视
setResizable(false); //设置不可更改大小
setDefaultCloseOperation(EXIT_ON_CLOSE); //在关闭窗体时,程序终止
}
private void GUIinit(){
setTitle("服务器"); //设置标题
setBounds(100,250,500,300); //位置与大小
Box boxH1=Box.createHorizontalBox(); //创建水平盒式容器,后续依次从左到右添加"Port:",text1,button1;
Box boxH2=Box.createHorizontalBox(); //创建水平盒式容器,后续依次从左到右添加"Say: ",text2,button2;
Box boxV=Box.createVerticalBox(); //创建垂直盒式容器,后续依次从垂直方向从上向下添加"服务器设置:",boxH1,boxH2
//1°开始添加“服务器设置:”标签至boxV
Box temp=Box.createHorizontalBox();
temp.add(new JLabel("服务器设置:"));
temp.add(Box.createHorizontalStrut(400)); //添加水平支撑(类似Qt中的弹簧)
boxV.add(temp);
boxV.add(Box.createVerticalStrut(5));//添加垂直支撑(类似Qt中的弹簧)
//2°开始填装boxH1,之后加入到boxV中
boxH1.add(new JLabel("Port:"));
boxH1.add(Box.createHorizontalStrut(5));
text1=new JTextField();
boxH1.add(text1);
boxH1.add(Box.createHorizontalStrut(10));
button1=new JButton("Start");
boxH1.add(button1);
boxV.add(boxH1);
boxV.add(Box.createVerticalStrut(10));
//3°开始添加文本显示区至boxV
text=new JTextArea(9,30);
JScrollPane jScrollPane=new JScrollPane(text); //将text放置到滚动窗格(一种容器)中
text.setEditable(false);
jScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
//当text内文本高度超过text高度时,text出现竖直滚动条
boxV.add(jScrollPane);
boxV.add(Box.createVerticalStrut(10));
//4°开始填装boxH2,之后将boxH2加入到boxV中
boxH2.add(new JLabel("Say:"));
boxH2.add(Box.createHorizontalStrut(5));
text2=new JTextField();
boxH2.add(text2);
boxH2.add(Box.createHorizontalStrut(5));
button2=new JButton("Say");
boxH2.add(button2);
boxV.add(boxH2);
add(boxV);
}
private void eventinit(){
ac=new myactionlistenr(); //初始化监听器
button1.addActionListener(ac); //监听器注册
button2.addActionListener(ac); //监听器注册
}
private void startserver(){ //”开启服务“方法,当点击Start按钮时,监听器检测到,调用此方法
try {
server = new ServerSocket(Integer.parseInt(text1.getText())); //用text1文本框内容对应的数字初始化服务器套接字
text.append("Server starting…\n"); //服务器端打印"Server starting…"
text.paintImmediately(text.getBounds()); //本条语句使得Server starting立即显示出来
// 原因在于append方法只是将参数加至缓冲区
// 而本线程会被下一行accept方法阻塞
//调用paintImmediately方法直接将缓冲区内容显示出来
sc = server.accept(); // 等待客户端连接,初始化与客服端相连接的套接字对象sc
text.append("Client connected…\n"); //服务器端打印”Client connected…“
in = new DataInputStream(sc.getInputStream()); //将in初始化为sc的输入流
out = new DataOutputStream(sc.getOutputStream()); //将out初始化为sc的输出流
readThread = new ReadThread(); //创建读取数据的线程
readThread.start(); //读取数据的线程开始运行
} catch (IllegalArgumentException e) { //若端口参数不符合,抛出IllegalArgumentException,用消息对话框提示用户
JOptionPane.showMessageDialog(this,"端口值范围必须为0~65535","警告",JOptionPane.WARNING_MESSAGE);
}catch (IOException e){
JOptionPane.showMessageDialog(this,"未知原因连接错误","警告",JOptionPane.WARNING_MESSAGE);
}catch (Exception e){
System.out.println(e.toString());
}
}
private void sentmessage(){ //当say按钮被点击,监听器检测到,调用该方法
if(readThread==null){ //先判断是否已连接,只需判断readThread是否被创建,未创建则发出提示信息,终止方法
JOptionPane.showMessageDialog(this,"未连接无法发送信息","警告",JOptionPane.WARNING_MESSAGE);
return;
}
try{
out.writeUTF("Server: "+text2.getText()+"\n"); //向客户端发送信息
text.append("Server: "+text2.getText()+"\n"); //同时更新服务器端文本区内容
text2.setText(""); //清空text2
}catch (IOException e){//异常处理
JOptionPane.showMessageDialog(this,"未知原因无法发送信息","警告",JOptionPane.WARNING_MESSAGE);
System.out.println(e.toString());
}
}
class myactionlistenr implements ActionListener { //监视按钮的监听器
public void actionPerformed(ActionEvent e) {
if (e.getSource() == button1) startserver(); //如果点击的按钮为start,调用startserver方法
if (e.getSource() == button2) sentmessage(); //如果点击的按钮为say,则调用sentmessage方法
}
}
class ReadThread extends Thread{
public void run(){
while(true) {
try {
text.append(in.readUTF()); //将输入流读取的数据添加至文本显示区
} catch (IOException e) {
System.out.println(e.toString());
}
}
}
}
public static void main(String[] args) {
ServerFrame sf=new ServerFrame();
}
}
②客户端:
//Author:Sherlock Novitch in Hfut
// -*- codeing=utf-8 -*-
// @time:2020/10/19 15:14
// @File:ClientFrame.java
// @Software:IDEA
// @blog:https://blog.csdn.net/RealCoder
package experiment2;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.*;
public class ClientFrame extends JFrame {
private JTextField text1,text2,text3; //text1为ip地址输入栏,text2为端口地址输入栏,text3为消息输入栏
private JTextArea text; //文本显示区
private JButton button1,button2; //button1为connect按钮,button2为say按钮
private Socket client; //客户端套接字
private DataInputStream in; //从服务器到客户端的输入流
private DataOutputStream out; //从客户端到服务器的输出流
private ReadThread readThread; //读取服务器消息的线程
private actionlistener ac; //监听按钮button1和button2的监听器
private ClientFrame() { //构造方法
setLayout(new FlowLayout()); //窗体整体布局为FlowLayout
GUIinit(); //调用组件初始化方法
Eventinit(); //调用事件初始化方法
setVisible(true); //设置可见
setResizable(false); //设置不可更改大小
setDefaultCloseOperation(EXIT_ON_CLOSE); //设置当关闭窗口时,程序终止
}
private void GUIinit(){ //组件初始化方法
setTitle("客户端"); //设置标题
setBounds(600,250,500,300); //位置大小
Box boxH1=Box.createHorizontalBox(); //创建水平盒式容器,后续装入"Server IP:",text1,"Server Port",button1;
Box boxH2=Box.createHorizontalBox(); //创建水平盒式容器,后续装入"Say:",text3,button2;
Box boxV=Box.createVerticalBox(); //创建垂直盒式容器,后续装入“客户端设置:",boxH1,text,boxH2;
//1°boxV装入“客户端设置”标签
Box temp=Box.createHorizontalBox();
temp.add(new JLabel("客户端设置:"));
temp.add(Box.createHorizontalStrut(400));
boxV.add(temp);
boxV.add(Box.createVerticalStrut(5));
//2°将Server IP:",text1,"Server Port",button1从左到右加入到boxH1中,再讲boxH1加入到boxV中
boxH1.add(new JLabel("Server IP:"));
boxH1.add(Box.createHorizontalStrut(5));
text1=new JTextField();
boxH1.add(text1);
boxH1.add(Box.createHorizontalStrut(10));
boxH1.add(new JLabel("Server Port:"));
boxH1.add(Box.createHorizontalStrut(5));
text2=new JTextField();
boxH1.add(text2);
boxH1.add(Box.createHorizontalStrut(5));
button1=new JButton("Connect");
boxH1.add(button1);
boxV.add(boxH1);
boxV.add(Box.createVerticalStrut(10));
//3°将text文本显示区加入到滚动窗格并设置超高度出现竖直滚动条,再将滚动窗口加入到boxV中
text=new JTextArea(9,30);
JScrollPane jScrollPane=new JScrollPane(text);
text.setEditable(false);
jScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
boxV.add(jScrollPane);
boxV.add(Box.createVerticalStrut(10));
//4°依次将从左到右“客户端设置:",boxH1,text,boxH2加入到boxH2中,再将boxH3加入到boxV中
boxH2.add(new JLabel("Say:"));
boxH2.add(Box.createHorizontalStrut(5));
text3=new JTextField();
boxH2.add(text3);
boxH2.add(Box.createHorizontalStrut(5));
button2=new JButton("Say");
boxH2.add(button2);
boxV.add(boxH2);
add(boxV);
}
private void Eventinit(){
ac=new actionlistener();//初始化监听器
button1.addActionListener(ac); //监听器注册
button2.addActionListener(ac); //监听器注册
}
private void startserver(){ //点击connect按钮后调用的启动客户端服务的方法
try{
client=new Socket(); //初始化客户端套接字
InetAddress address=InetAddress.getByName(text1.getText()); //用text1文本栏中的ip地址创建InetAddress
InetSocketAddress socketAddress=new InetSocketAddress(address,Integer.parseInt(text2.getText())); //所连接服务器的地址
client.connect(socketAddress); //连接到服务器
text.append("Connect to server…\n"); //文本显示区添加“Connect to server…”
in=new DataInputStream(client.getInputStream()); //初始化输入流
out=new DataOutputStream(client.getOutputStream()); //初始化输出流
readThread=new ReadThread(); //创建读取数据的线程
readThread.start(); //线程开始运行
}catch (UnknownHostException e){ //IP地址有误,发出提示
JOptionPane.showMessageDialog(this,"输入IP地址有误","警告",JOptionPane.WARNING_MESSAGE);
}catch (IllegalArgumentException e) { //端口值非法,发出提示
JOptionPane.showMessageDialog(this,"端口值范围必须为0~65535","警告",JOptionPane.WARNING_MESSAGE);
}
catch (IOException e) {
JOptionPane.showMessageDialog(this,"无法连接至端口","警告",JOptionPane.WARNING_MESSAGE);
}catch (Exception e){
System.out.println(e.toString());
}
}
private void sentmessage(){ //点击say按钮调用的方法
if(readThread==null){ //先判断是否已经连接至服务器,若没有,提示并终止方法
JOptionPane.showMessageDialog(this,"未连接无法发送信息","警告",JOptionPane.WARNING_MESSAGE);
return;
}
try{
out.writeUTF("Client: "+text3.getText()+"\n"); //发送信息至服务器
text.append("Client: "+text3.getText()+"\n"); //客户端文本显示区添加刚发过的内容
text3.setText(""); //清空输入栏
}catch (IOException e){
JOptionPane.showMessageDialog(this,"未知原因无法发送信息","警告",JOptionPane.WARNING_MESSAGE);
}
}
class actionlistener implements ActionListener{
public void actionPerformed(ActionEvent e){
if(e.getSource()==button1) startserver(); //如果点击的按钮是connect(button1),调用startserver方法
if(e.getSource()==button2) sentmessage(); //如果点击的按钮是say(button2),调用sentmessage方法
}
}
class ReadThread extends Thread{ //读取数据的线程
public void run(){
while(true){
try{
text.append(in.readUTF()); //读入服务器传入的信息,并显示到文本显示区
}catch (IOException e) {
System.out.println(e.toString());
}
}
}
}
public static void main(String[] args) {
ClientFrame cf=new ClientFrame();
}
}
卑微求赞