利用java编写网络聊天程序并加密信息
——————————————————————————————分割线————————
看链接里面的文章,是我重构后代码,比这篇文章的破代码强多了
优化了代码后的地址:https://blog.csdn.net/qq_43483251/article/details/125470514?spm=1001.2014.3001.5501
——————————分割线—————————————
所用知识:javaGUI+网络编程+多线程+信息加密
**原理:**GUI编写基本的图形化界面,利用UDP传输信息,利用多线程同时接收以及发送信息,利用信息加密,加密,密码以及聊天信息。
可能会遇到的问题:
2.javaGUI中的重画方法,repaint只能在原有基础上重绘,并不能清除原来痕迹。因为我使用的Swing默认为轻量级则不执行update,awt组件才是重量级。解决方法:
removeAll()移除组件,repaint()刷新面板,再添加组件( add() ),最后重新布局面板( revalidate() )。
3.只有字符串类型的相同变量的哈希值才会相同,因为字符串都存储在同一内存池中,且每定义一个字符串变量会先判断内存池中是否有相同内容的,如果有就不会开辟新的空间,而是直接将变量指向此内容。
4.网络编程中接收的消息被存储进字节数组中,再次拿出来时其实还附带了空余的字节数组空间中的默认值:0,需要对字节数组做判断并只拿取有效的字符串信息。
代码:
登陆界面代码:
package MyNetworkSecurity01;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.*;
public class Land {
//用来当按钮按下后赋值的变量,控制客户端的弹出
int i=0;
JFrame frame=new JFrame();
//设置面板,为了显示标签
JPanel panel=new JPanel();
//设置账号,密码的标签
JLabel land =new JLabel("请登录或者注册!");
JLabel accountNumber =new JLabel("账号:");
JLabel password=new JLabel("密码:");
//设置标签旁的文本框,因为要观察加密情况,所以密码框也使用普通文本框
JTextField accountNumber02 = new JTextField(20);
JTextField password02 = new JTextField(20);
//设置登陆和注册按钮!
JButton landButton= new JButton("登陆");
JButton registerButton = new JButton("注册");
//显示登陆成功的标签
JLabel label = new JLabel( "登陆成功!");
//登陆成功后跳转的按钮
JButton Jump=new JButton("继续");
//构造方法
Land(){
//设置界面居中
frame.setBounds(Toolkit.getDefaultToolkit().getScreenSize().width/2-200,Toolkit.getDefaultToolkit().getScreenSize().height/2-250,400,500);
//设置程序标题
frame.setTitle(" 登陆界面");
//设置关闭
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//设置容器
Container container=frame.getContentPane();
//设置面板的布局
panel.setLayout(null);
//将面板放入容器
container.add(panel);
//设置标签位置
land.setBounds(100,25,200,50);
accountNumber.setBounds(50,75,50,50);
password.setBounds(49,150,50,50);
//设置字体的样式:行楷,粗体,大小
land.setFont(new Font("行楷",Font.BOLD,20));
accountNumber.setFont(new Font("行楷",Font.BOLD,20));
password.setFont(new Font("行楷",Font.BOLD,20));
//将标签放入面板
panel.add(land);
panel.add(accountNumber);
panel.add(password);
//设置文本框的位置
accountNumber02.setBounds(100,85,200,30);
password02.setBounds(99,160,200,30);
//将文本框放入面板
panel.add(accountNumber02);
panel.add(password02);
//设置按钮的位置和大小
landButton.setBounds(70,300,80,35);
registerButton.setBounds(230,300,80,35);
//将按钮添加进面板
panel.add(landButton);
panel.add(registerButton);
Button_ button_ = new Button_();//创建按钮点击事件实现类的对象
//设置按钮的监听事件
//登陆的监听事件
landButton.addActionListener(button_);
//注册的监听事件
registerButton.addActionListener(button_);
Jump.addActionListener(new Button_01());
//文本框的事件监听
//账号框
accountNumber02.addActionListener(button_);
//密码框
password02.addActionListener(button_);
frame.setVisible(true);
}
//用成员内部类实现按钮点击事件
class Button_ implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//获取账号框的内容
String accountNumber_Button = accountNumber02.getText();
accountNumber02.setText("");//清空账号框
//获取密码框内容
String password_Button = (password02.getText());
password02.setText("");//清空密码框
//确认账号,密码输入不为空
if (accountNumber_Button.equals("") || password_Button.equals("")) {
JLabel password03 = new JLabel("抱歉,框不能为空!");
password03.setForeground(Color.red);//设置字体颜色
password03.setBounds(70, 350, 250, 40);
password03.setFont(new Font("行楷", Font.BOLD, 20));
panel.removeAll();//移除组件
panel.repaint();//刷新移除组件后的效果组件
//重新添加组件
panel.add(land);
panel.add(accountNumber);
panel.add(password);
panel.add(accountNumber02);
panel.add(password02);
panel.add(landButton);
panel.add(registerButton);
panel.add(password03);
//重新布局面板
panel.revalidate();
} else {
String hashCode_password="";
//将接收到的密码转换成字符数组进行倒置
//将获取的密码(字符串)变成字符数组
char[] hashCode_password01 =password_Button.toCharArray();
//倒置字符数组并转换成字符串,如:第一个变为最后一个。
for (int length = hashCode_password01.length-1; length >=0; length--) {
hashCode_password=hashCode_password+hashCode_password01[length];
}
//计算密码的字符串形式的哈希值,并重新赋值给hashCode_password(字符串变量)
int i = hashCode_password.hashCode();
hashCode_password=""+i;
//处理登陆和注册问题
if (e.getActionCommand().equals("登陆")) {
try {
TextSend(accountNumber_Button,hashCode_password);
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
//设置显示点击按钮后加密后的密码
JLabel password03 = new JLabel("加密后的密码是:");
password03.setForeground(Color.red);//设置字体颜色
password03.setBounds(70, 100, 200, 40);
password03.setFont(new Font("行楷", Font.BOLD, 20));
JTextField password_text = new JTextField();//用来显示加密后的密码
password_text.setBounds(70, 150, 250, 35);
password_text.setText(""+hashCode_password);
panel.removeAll();//移除组件
panel.repaint();//刷新移除组件后的效果组件
//重新添加组件
panel.add(password03);
panel.add(password_text);
panel.add(label);
panel.add(Jump);
//重新布局面板
panel.revalidate();
}
}
}
class Button_01 implements ActionListener{
@Override
public void actionPerformed(ActionEvent e1) {
//设置登陆后的跳转按钮点击事件
if(e1.getActionCommand().equals("继续")){
frame.setVisible(false);
//对信息加密解密的类
EncryptionAndDecryption ed = new EncryptionAndDecryption();
YouClient youClient = new YouClient();
//接收
Runnable runnable = () -> {//定义自身名字 //定义对方的端口号
//建立进程要用的端口
DatagramSocket socket = null;
try {
socket = new DatagramSocket(9100);
} catch (SocketException e) {
e.printStackTrace();
}
// 设置一个接收数据的字符串
String data;
while (true) {
//设置一次接收多少的字节byte数据
byte[] data_byte = new byte[100];//单位是字节
//构造一个 DatagramPacket用于接收长度的数据包 length 。
DatagramPacket packet = new DatagramPacket(data_byte, 0, data_byte.length);
//接收来自DatagramPacket的数据包
try {
socket.receive(packet);
} catch (IOException e) {
e.printStackTrace();
}
String s3="";
//存储接收到的信息
char[] s2 = (new String(packet.getData(), 0, packet.getData().length)).toCharArray();
for(char s1:s2){
if(s1==0)break;
s3=s3+s1;
}
//在界面上显示收到的信息
youClient.receive_text01.setText(s3);
//解密
ed.Decryption(s3);
youClient.receive_text02.setText(ed.data02);
//获得包裹中的数据
data = "服务端:" + " " +ed.data02;
if (!data.equals("")) {
youClient.str = youClient.str + data + "\n";
youClient.ChatBox.setText(youClient.str);
}
youClient.connect.setText("连接成功!");
}
};
//接收
Thread thread = new Thread(runnable);
thread.start();
}
}
}
public static void main(String[] args) {
new Land();
}
//用来发送登陆请求并接收回执/或者发送消息
public void TextSend(String accountNumber,String password ) throws IOException {
//建立端口
DatagramSocket socket=new DatagramSocket(9090);
//获取本机地址
InetAddress inetAddress=InetAddress.getByName("localhost");
//设置另一个的端口号
int port=5051;
//账号和密码的数据
String account="账号"+accountNumber+"密码"+password;
//发送数据需要的,数据,地址,端口号
DatagramPacket packet=new DatagramPacket(account.getBytes(),0,account.getBytes().length,inetAddress,port);
//打包发送
socket.send(packet);
//接收回执
//接收数据包
byte[] buffer=new byte[1024];
//构造一个 DatagramPacket用于接收长度的数据包 length 。
DatagramPacket packet1=new DatagramPacket(buffer,0,buffer.length);
//接收来自DatagramPacket的数据包
socket.receive(packet1);
String receive = new String(packet1.getData(), 0, "登陆成功!".getBytes().length);
if(receive.equals("登陆成功!")){
try {
label.setBounds(120,190,250,30);
label.setFont(new Font("行楷", Font.BOLD, 30));
Jump.setBounds(140,350,100,30);
} catch (Exception e) {
e.printStackTrace();
}
}
//关闭流
socket.close();
}
}
服务端代码:
package MyNetworkSecurity01;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.*;
public class MyServer {
//用来存储聊天信息
static String str="";
//用来接收输入框中的信息
static String str1="";
JFrame frame=new JFrame();
//设置最内层面板
JPanel panel=new JPanel();
//设置panel_text聊天面板的组件
JTextArea ChatBox=new JTextArea(20,10);//文本域,聊天记录显示
//设置显示对话内容的面板
JScrollPane panel_text=new JScrollPane(ChatBox);
//设置容器
Container container=frame.getContentPane();
//panel的组件
JTextField connect = new JTextField();
//标签
JLabel receive01=new JLabel("收到的加密信息:");
JLabel receive02=new JLabel("解译后:");
JLabel send_out01=new JLabel("想发送的文字:");
JLabel send_out02=new JLabel("加密后:");
//文本框
JTextField receive_text01=new JTextField();
JTextField receive_text02=new JTextField();
JTextField send_out_text01=new JTextField();
JTextField send_out_text02=new JTextField();
JTextField ChatText=new JTextField();//聊天输入框
JButton inButton=new JButton("发送"); //发送信息按钮
public void init(){
//设置界面居中
frame.setBounds(Toolkit.getDefaultToolkit().getScreenSize().width/2-300,Toolkit.getDefaultToolkit().getScreenSize().height/2-350,600,700);
//设置程序标题
frame.setTitle("服务端");
//设置关闭
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//设置面板的布局
panel.setLayout(null);
//设置聊天面板的大小,位置
panel_text.setBounds(200,0,380,500);
//将面板放入容器
container.add(panel);
panel.add(panel_text);
//设置最内层面板的组件
connect.setBounds(20,50,150,30);
receive01.setBounds(20,130,180,30);
receive01.setFont(new Font("行楷",Font.BOLD,20));
receive01.setForeground(Color.red);//设置字体颜色
receive_text01.setBounds(20,160,150,30);
receive_text01.setFont(new Font("行楷",Font.BOLD,20));
receive02.setBounds(20,190,150,30);
receive02.setFont(new Font("行楷",Font.BOLD,20));
receive02.setForeground(Color.red);
receive_text02.setBounds(20,220,150,30);
receive_text02.setFont(new Font("行楷",Font.BOLD,20));
send_out01.setBounds(20,300,150,30);
send_out01.setFont(new Font("行楷",Font.BOLD,20));
send_out01.setForeground(Color.blue);//设置字体颜色
send_out_text01.setBounds(20,330,150,30);
send_out_text01.setFont(new Font("行楷",Font.BOLD,20));
send_out02.setBounds(20,360,150,30);
send_out02.setFont(new Font("行楷",Font.BOLD,20));
send_out02.setForeground(Color.blue);//设置字体颜色
send_out_text02.setBounds(20,390,150,30);
send_out_text02.setFont(new Font("行楷",Font.BOLD,20));
//聊天输入框设置
ChatText.setBounds(200,500,300,30);
//聊天框的发送按钮
inButton.setBounds(500,500,100,30);
//聊天域的字体,大小设置
ChatBox.setFont(new Font("行楷",Font.BOLD,20));
panel.add(connect);
panel.add(receive01);
panel.add(receive_text01);
panel.add(receive02);
panel.add(receive_text02);
panel.add(send_out01);
panel.add(send_out_text01);
panel.add(send_out02);
panel.add(send_out_text02);
panel.add(ChatText);
panel.add(inButton);
frame.setVisible(true);
//发送按钮的监听事件
inButton.addActionListener(new InButton());
}
//成员内部类实现接口且发发送数据
class InButton implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//获取聊天输入框的内容
str1 = ChatText.getText();
ChatText.setText("");
str=str+"我: "+str1+"\n";
ChatBox.setText(str);
//加密前的信息到界面显示
send_out_text01.setText(str1);
//对信息加密解密的类
EncryptionAndDecryption ed = new EncryptionAndDecryption();
//加密
ed.Encryption(str1);
// 将加密后的信息发送到界面显示
send_out_text02.setText(ed.data01);
try {
new UDP_Send(5656, 9100,ed.data01);
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
//处理登陆或者注册问题
public void landAndRegister() throws IOException {
//在本机地址上建立端口,以接收
DatagramSocket socket=new DatagramSocket(5051);
//接收数据包
byte[] buffer=new byte[1024];
//构造一个 DatagramPacket用于接收长度的数据包 length 。
DatagramPacket packet=new DatagramPacket(buffer,0,buffer.length);
//接收来自DatagramPacket的数据包
socket.receive(packet);
System.out.println("发送的内容:"+new String(packet.getData(),0,packet.getData().length));
if(packet.getData()!=null){
connect.setText("连接成功!");
//获取本机地址
InetAddress inetAddress= InetAddress.getByName("localhost");
int port=9090;
//参数:数据(Byte类型),发送数据的长度,要发给谁
DatagramPacket packet1=new DatagramPacket("登陆成功!".getBytes(),0,"登陆成功!".getBytes().length,inetAddress,port);
socket.send(packet1);//发送包
}
//关闭流
socket.close();
}
MyServer( ) throws IOException {
init();
landAndRegister();
}
public static void main(String[] args) throws IOException {
MyServer myServer = new MyServer();
//对信息加密解密的类
EncryptionAndDecryption ed = new EncryptionAndDecryption();
//接收
Runnable runnable = () -> {//定义自身名字 //定义自己的端口号
//建立进程要用的端口
DatagramSocket socket = null;
try {
socket = new DatagramSocket(5052);
} catch (SocketException e) {
e.printStackTrace();
}
// 设置一个接收数据的字符串
String data;
while (true) {
//设置一次接收多少的字节byte数据
byte[] data_byte = new byte[100];//单位是字节
//构造一个 DatagramPacket用于接收长度的数据包 length 。
DatagramPacket packet = new DatagramPacket(data_byte, 0, data_byte.length);
//接收来自DatagramPacket的数据包
try {
socket.receive(packet);
} catch (IOException e) {
e.printStackTrace();
}
//因为接收的信息的字节数组长度很长,除了信息外还有很多无用的默认值为:0
//的数据在数组中,需要加以判断,哪些是有用的,如果不进行判断,输出来的信息
//会带很长的正方形的小尾巴
String s3="";
//存储接收到的信息
char[] s2 = (new String(packet.getData(), 0, packet.getData().length)).toCharArray();
for(char s1:s2){
if(s1==0)break;
s3=s3+s1;
}
//在界面上显示收到的信息
myServer.receive_text01.setText(s3);
//解密
ed.Decryption(s3);
myServer.receive_text02.setText(ed.data02);
//设置显示在文本域中的信息
data = "客户端:" + " "+ ed.data02;
if (!data.equals("")) {
str = str + data + "\n";
myServer.ChatBox.setText(str);
}
}
};
//接收
Thread thread = new Thread(runnable);
thread.start();
}
}
客户端代码:
package MyNetworkSecurity01;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
public class YouClient {
//用来存储聊天信息
static String str="";
//用来接收输入框中的信息
static String str1="";
JFrame frame=new JFrame();
//设置最内层面板
JPanel panel=new JPanel();
//设置panel_text聊天面板的组件
JTextArea ChatBox=new JTextArea(20,10);//文本域,聊天记录显示
//设置显示对话内容的面板
JScrollPane panel_text=new JScrollPane(ChatBox);
//设置容器
Container container=frame.getContentPane();
//panel的组件
JTextField connect = new JTextField();
//标签
JLabel receive01=new JLabel("收到的加密信息:");
JLabel receive02=new JLabel("解译后:");
JLabel send_out01=new JLabel("想发送的文字:");
JLabel send_out02=new JLabel("加密后:");
//文本框
JTextField receive_text01=new JTextField();
JTextField receive_text02=new JTextField();
JTextField send_out_text01=new JTextField();
JTextField send_out_text02=new JTextField();
JTextField ChatText=new JTextField();//聊天输入框
JButton inButton=new JButton("发送"); //发送信息按钮
public void init(){
//设置界面居中
frame.setBounds(Toolkit.getDefaultToolkit().getScreenSize().width/2-300,Toolkit.getDefaultToolkit().getScreenSize().height/2-350,600,700);
//设置程序标题
frame.setTitle("客户端");
//设置关闭
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//设置面板的布局
panel.setLayout(null);
//设置聊天面板的大小,位置
panel_text.setBounds(200,0,380,500);
//将面板放入容器
container.add(panel);
panel.add(panel_text);
//设置最内层面板的组件
connect.setBounds(20,50,150,30);
receive01.setBounds(20,130,180,30);
receive01.setFont(new Font("行楷",Font.BOLD,20));
receive01.setForeground(Color.red);//设置字体颜色
receive_text01.setBounds(20,160,150,30);
receive_text01.setFont(new Font("行楷",Font.BOLD,20));
receive02.setBounds(20,190,150,30);
receive02.setFont(new Font("行楷",Font.BOLD,20));
receive02.setForeground(Color.red);
receive_text02.setBounds(20,220,150,30);
receive_text02.setFont(new Font("行楷",Font.BOLD,20));
send_out01.setBounds(20,300,150,30);
send_out01.setFont(new Font("行楷",Font.BOLD,20));
send_out01.setForeground(Color.blue);//设置字体颜色
send_out_text01.setBounds(20,330,150,30);
send_out_text01.setFont(new Font("行楷",Font.BOLD,20));
send_out02.setBounds(20,360,150,30);
send_out02.setFont(new Font("行楷",Font.BOLD,20));
send_out02.setForeground(Color.blue);//设置字体颜色
send_out_text02.setBounds(20,390,150,30);
send_out_text02.setFont(new Font("行楷",Font.BOLD,20));
//聊天输入框设置
ChatText.setBounds(200,500,300,30);
//聊天框的发送按钮
inButton.setBounds(500,500,100,30);
//聊天域的字体,大小设置
ChatBox.setFont(new Font("行楷",Font.BOLD,20));
panel.add(connect);
panel.add(receive01);
panel.add(receive_text01);
panel.add(receive02);
panel.add(receive_text02);
panel.add(send_out01);
panel.add(send_out_text01);
panel.add(send_out02);
panel.add(send_out_text02);
panel.add(ChatText);
panel.add(inButton);
frame.setVisible(true);
//发送按钮的监听事件
inButton.addActionListener(new InButton());
}
//成员内部类实现接口且发发送数据
class InButton implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//获取聊天输入框的内容
str1 = ChatText.getText();
send_out_text01.setText(str1);
//对信息加密解密的类
EncryptionAndDecryption ed = new EncryptionAndDecryption();
//加密
ed.Encryption(str1);
// 将加密后的信息发送到界面显示
send_out_text02.setText(ed.data01);
ChatText.setText("");
str=str+"我: "+str1+"\n";
ChatBox.setText(str);
try {
new UDP_Send(9099, 5052,ed.data01);
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
YouClient(){
init();
}
}
UDP发送代码:
package MyNetworkSecurity01;
import java.io.IOException;
import java.net.*;
//发送
public class UDP_Send {
//定义对方的端口号
public int MyPort,YouPort;
//建立进程要用的端口
DatagramSocket socket=new DatagramSocket(MyPort);
//获取本机ip
InetAddress inetAddress= InetAddress.getByName("localhost");
//要传输的数据
private String data;
public UDP_Send(int MyPort,int YouPort,String data) throws IOException {
this.MyPort=MyPort;
this.YouPort=YouPort;
this.data=data;
if(!this.data.equals("")) {
//准备好要发的数据,目的地的ip地址,进程端口号
DatagramPacket packet = new DatagramPacket(this.data.getBytes(), 0, this.data.getBytes().length, inetAddress, this.YouPort);
//发送
try {
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
socket.close();
this.data="";//清空要传输的数据数据
}
}
}
加密与解密代码:
package MyNetworkSecurity01;
import java.util.Random;
public class EncryptionAndDecryption {
//设置接收最终加密后信息的字符串变量
String data01="";
//设置接收最终解密后信息的字符串变量
String data02="";
//加密
public void Encryption(String encryption){
data01="";
Random r=new Random();
//随机取0-10一个数作为偏移量
int Offset01 =r.nextInt(10);
//将需要加密的字符串变为字符数组,并且将偏移量作为字符数组的第一个数
char[] char01=(Offset01+encryption).toCharArray();
/*加入在字符数组的每个数中加入偏移量,使之改变为其他数,
如:a+3(3为偏移量)=d,这样加密了信息
*/
for (int i = 0; i < char01.length; i++) {
data01=data01+(char) ((int)char01[i]+Offset01);
}
}
//解密
public void Decryption(String decryption){
data02="";
//将得到的信息变成字符数组
char[] char02 = decryption.toCharArray();
//获取偏移量
int Offset02= ((int)char02[0]-48)/2;
//利用循环和偏移量解出信息,并保存在字符数组中
for(int i=1;i<char02.length;i++){
data02=data02+(char) (char02[i]-Offset02);
}
}
}