Swing聊天软件
前面做了Tcp和Udp的基本通信以及文件传输实验,是时候把这些内容整合在一起了。。
Swing就不介绍了,需要用到什么api都可以直接查。
一、基本聊天功能(服务端作中介,udp实现)
软件能够实现Windows主机和一台Linux虚拟机的通信。除此之外还需要一台Linux虚拟机作为服务端起到二者中介的作用。两个客户端与服务器的通信采用无连接的udp协议,服务器收到udp包直接向目标端口转发,不做其他任何操作。客户端在发送按钮事件里编写socket.send,同时new一个Thread并在此进程里编写socket.recieve,只有采用多线程才能保证聊天时能够同时收发信息。以上便可实现基本的聊天功能。
注意Win和Linux二者的编码有别。即
//Win客户端
byte[] buffer = text.getBytes("UTF-8");
...
//Linux客户端
byte[] buffer = text.getBytes("GBK");
二、文件传输功能(端到端,tcp实现)
文件传输采用tcp协议直接由端到端传输,这样的好处是传输效率高而且可靠、面向连接。除此之外,还需要多加一个收发信的确认功能。为了避免占用太多端口以及浪费过多线程,这里收发信还是使用原来的udp端口(而不是新建一个端口+线程)。那么就需要设计一个密文放在消息的开头以区分正常的聊天消息(我用的是三个井号"###"也可以更复杂点)。然后该密文还需要携带一些重要信息,包括:文件类型,文件名称,文件大小,主机ip以及端口号,后两者是为了告知目的主机使其与发送主机主动建立tcp连接(因为主机之间是不知道对方的ip和端口号的)。
文件传输的基本设计思路为:
发端:点击文件按钮→选择文件,获取文件、文件名等,发送密文,打开serversocket等待对方accept。
收端:收到udp消息,进行解析发现是密文,于是弹出对话框问是否接受。若选否则进行tcp连接,发送"no"消息,关闭socket。若选是则也进行tcp连接,发送"yes"消息,并准备接收文件。
发端:连接成功后,收到"no"则关闭连接,收到"yes"则准备发送文件。若是图片则使用字节流方法,文本文件则使用字符流方法。
其中传文本文件还要注意一下中文编码问题。在发端读取文件时,Win使用GBK编码,Linux使用UTF-8编码。在收端读取socket的inputStream时二者均使用UTF-8编码。
实验截图:
三、代码
1.Win客户端代码(ip:10.177.26.76,udp port:1080,tcp port:2080)
package gui;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
public class WinClient
{
public static void main(String[] args) throws Exception
{
JFrame f = new JFrame();
f.setTitle("16010410031 陈大至");
f.setSize(720, 810);
f.setLocation(100, 100);
f.setLayout(null);
JButton button = new JButton("发送");
button.setBounds(610, 650, 80, 50);
f.add(button);
JButton imgButton = new JButton("图片");
imgButton.setBounds(10,560,70,30);
f.add(imgButton);
JButton fileButton = new JButton("文本文件");
fileButton.setBounds(100,560,90,30);
f.add(fileButton);
JTextArea textArea1 = new JTextArea();
textArea1.setFont(new Font("Dialog", Font.TRUETYPE_FONT,20));
textArea1.setLineWrap(true);
textArea1.setBounds(10, 600, 590, 150);
f.add(textArea1);
JTextArea textArea2 = new JTextArea();
textArea2.setFont(new Font("Dialog", Font.TRUETYPE_FONT,20));
textArea2.setEditable(false);
textArea2.setLineWrap(true);
JScrollPane scrollPane = new JScrollPane(textArea2);
f.add(scrollPane);
textArea2.setBounds(10,10, 680, 540);
f.add(textArea2);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
String hostname="192.168.131.135";//192.168.131.135
String localhost="10.177.26.76";
DatagramSocket socket1 = new DatagramSocket(1080);
ServerSocket socket2 = new ServerSocket(2080);
new Thread(() -> {
while (true)
{
try {
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket1.receive(packet);
String text = new String(packet.getData(),0,packet.getLength());
String[] strings = text.split(";");
if(strings[0].equals("###"))
{
int option;
String name=strings[2];
String length=strings[3];
String ip=strings[4];
int port=Integer.parseInt(strings[5]);
option = JOptionPane.showConfirmDialog(f,"收到文件:"+name+",大小:"+length+"kb,是否接收?");
Socket socket = new Socket(ip,port);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
if(strings[1].equals("img"))
{
if(option==JOptionPane.OK_OPTION)
{
out.println("yes");
InputStream in = socket.getInputStream();
FileOutputStream fileOut = new FileOutputStream("statics/保存图片.png");
byte[] bytes = new byte[1024];
int len;
while((len = in.read(bytes))!=-1){
fileOut.write(bytes, 0, len);
}
JOptionPane.showMessageDialog(f,"文件接收成功。路径:statics/保存图片.png");
fileOut.close();
in.close();
}
else
{
out.println("no");
JOptionPane.showMessageDialog(f,"已取消接受文件。");
}
}
else if(strings[1].equals("file"))
{
if(option==JOptionPane.OK_OPTION)
{
out.println("yes");
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(),Charset.forName("UTF-8")));
PrintWriter fileOut = new PrintWriter("statics/"+name);
String s;
while((s = in.readLine())!=null)
{
System.out.println(s);
fileOut.println(s);
}
fileOut.close();
in.close();
JOptionPane.showMessageDialog(f,"文件接收成功。路径:statics/"+name);
}
else
{
out.println("no");
JOptionPane.showMessageDialog(f,"已取消接受文件。");
}
}
out.close();
socket.close();
}
else textArea2.append("对方:"+text+"\r\n");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
button.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
String text = textArea1.getText();
textArea2.append("你:"+text+"\r\n");
try {
byte[] buffer = text.getBytes("UTF-8");
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName(hostname),1082);
socket1.send(packet);
textArea1.setText("");
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
imgButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
try{
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setCurrentDirectory(new File("D:\\"));
fileChooser.setFileFilter(new FileNameExtensionFilter("Image Files","png","jpg","gif"));
int result = fileChooser.showOpenDialog(f);
if(result==JFileChooser.APPROVE_OPTION)
{
File file = fileChooser.getSelectedFile();
String filename = file.getName();
Long length = file.length()/1024;
String message = "###;img;"+filename+";"+length.toString()+";"+localhost+";2080";
byte[] buffer = message.getBytes();
DatagramPacket packet = new DatagramPacket(buffer,buffer.length,InetAddress.getByName(hostname),1082);
socket1.send(packet);
Socket socket = socket2.accept();
System.out.println("TCP连接成功");
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
if(reader.readLine().equals("yes"))
{
FileInputStream fileIn = new FileInputStream(file);
OutputStream out = socket.getOutputStream();
byte[] bytes = new byte[1024];
int len;
while((len = fileIn.read(bytes))!= -1)
{
out.write(bytes, 0, len);
}
JOptionPane.showMessageDialog(f,"文件传输成功。");
fileIn.close();
out.close();
}
else
{
JOptionPane.showMessageDialog(f,"文件被对方拒收。");
}
reader.close();
socket.close();
}
}catch (Exception e1){
e1.printStackTrace();
}
}
});
fileButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
try{
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setCurrentDirectory(new File("D:\\"));
fileChooser.setFileFilter(new FileNameExtensionFilter("Text Files","txt","doc","xls"));
int result = fileChooser.showOpenDialog(f);
if(result==JFileChooser.APPROVE_OPTION)
{
File file = fileChooser.getSelectedFile();
String filename = file.getName();
Long length = file.length()/1024;
String message = "###;file;"+filename+";"+length.toString()+";"+localhost+";2080";
byte[] buffer = message.getBytes();
DatagramPacket packet = new DatagramPacket(buffer,buffer.length,InetAddress.getByName(hostname),1082);
socket1.send(packet);
Socket socket = socket2.accept();
System.out.println("TCP连接成功");
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
if(reader.readLine().equals("yes"))
{
BufferedReader fileIn = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("GBK")));
PrintWriter out = new PrintWriter(socket.getOutputStream(),true);
String s;
while((s = fileIn.readLine())!=null)
{
System.out.println(s);
out.println(s);
}
JOptionPane.showMessageDialog(f,"文件传输成功。");
fileIn.close();
out.close();
}
else
{
JOptionPane.showMessageDialog(f,"文件被对方拒收。");
}
reader.close();
socket.close();
}
}catch (Exception e1){
e1.printStackTrace();
}
}
});
}
}
2.Linux客户端代码(ip:192.168.131.131,udp port:1081,tcp port:2081)
package gui;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.*;
import java.nio.charset.Charset;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
public class LinuxClient
{
public static void main(String[] args) throws Exception
{
JFrame f = new JFrame();
f.setTitle("16010410031 陈大至");
f.setSize(700, 790);
f.setLocation(900, 100);
f.setLayout(null);
JButton button = new JButton("发送");
button.setBounds(610, 650, 80, 50);
f.add(button);
JButton imgButton = new JButton("图片");
imgButton.setBounds(10,560,70,30);
f.add(imgButton);
JButton fileButton = new JButton("文本文件");
fileButton.setBounds(100,560,90,30);
f.add(fileButton);
JTextArea textArea1 = new JTextArea();
textArea1.setFont(new Font("Dialog", Font.TRUETYPE_FONT,20));
textArea1.setLineWrap(true);
textArea1.setBounds(10, 600, 590, 150);
f.add(textArea1);
JTextArea textArea2 = new JTextArea();
textArea2.setFont(new Font("Dialog", Font.TRUETYPE_FONT,20));
textArea2.setEditable(false);
textArea2.setLineWrap(true);
JScrollPane scrollPane = new JScrollPane(textArea2);
f.add(scrollPane);
textArea2.setBounds(10,10, 680, 540);
f.add(textArea2);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
String hostname = "192.168.131.135";//192.168.131.135
String localhost="192.168.131.131";//192.168.131.131
DatagramSocket socket1 = new DatagramSocket(1081);
ServerSocket socket2 = new ServerSocket(2081);
new Thread(() -> {
while (true)
{
try {
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket1.receive(packet);
String text = new String(packet.getData(),0,packet.getLength());
String[] strings = text.split(";");
if(strings[0].equals("###"))
{
int option;
String name=strings[2];
String length=strings[3];
String ip=strings[4];
int port=Integer.parseInt(strings[5]);
option = JOptionPane.showConfirmDialog(f,"收到文件:"+name+",大小:"+length+"kb,是否接收?");
Socket socket = new Socket(ip,port);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
if(strings[1].equals("img"))
{
if(option==JOptionPane.OK_OPTION)
{
out.println("yes");
InputStream inputStream = socket.getInputStream();
FileOutputStream fileOut = new FileOutputStream("statics/保存图片.png");
byte[] bytes = new byte[1024];
int len;
while((len = inputStream.read(bytes))!=-1){
fileOut.write(bytes, 0, len);
}
JOptionPane.showMessageDialog(f,"文件接收成功。路径:statics/保存图片.png");
fileOut.close();
socket.close();
}
else
{
out.println("no");
JOptionPane.showMessageDialog(f,"已取消接受文件。");
socket.close();
}
}
else if(strings[1].equals("file"))
{
if(option==JOptionPane.OK_OPTION)
{
out.println("yes");
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(),Charset.forName("UTF-8")));
PrintWriter fileOut = new PrintWriter("statics/"+name);
String s;
while((s = in.readLine())!=null)
{
System.out.println(s);
fileOut.println(s);
}
fileOut.close();
in.close();
JOptionPane.showMessageDialog(f,"文件接收成功。路径:statics/"+name);
}
else
{
out.println("no");
JOptionPane.showMessageDialog(f,"已取消接受文件。");
}
}
out.close();
socket.close();
}
else textArea2.append("对方:"+text+"\r\n");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
button.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
String text = textArea1.getText();
textArea2.append("你:"+text+"\r\n");
try {
byte[] buffer = text.getBytes("GBK");
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName(hostname),1082);
socket1.send(packet);
textArea1.setText("");
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
imgButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
try{
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setCurrentDirectory(new File("."));
fileChooser.setFileFilter(new FileNameExtensionFilter("Image Files","png","jpg","gif"));
int result = fileChooser.showOpenDialog(f);
if(result==JFileChooser.APPROVE_OPTION)
{
File file = fileChooser.getSelectedFile();
String filename = file.getName();
Long length = file.length()/1024;
String message = "###;img;"+filename+";"+length.toString()+";"+localhost+";2081";
byte[] buffer = message.getBytes();
DatagramPacket packet = new DatagramPacket(buffer,buffer.length,InetAddress.getByName(hostname),1082);
socket1.send(packet);
Socket socket = socket2.accept();
System.out.println("TCP连接成功");
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
if(reader.readLine().equals("yes"))
{
FileInputStream fileIn = new FileInputStream(file);
OutputStream out = socket.getOutputStream();
byte[] bytes = new byte[1024];
int len;
while((len = fileIn.read(bytes))!= -1)
{
out.write(bytes, 0, len);
}
JOptionPane.showMessageDialog(f,"文件传输成功。");
fileIn.close();
out.close();
}
else
{
JOptionPane.showMessageDialog(f,"文件被对方拒收。");
}
reader.close();
socket.close();
}
}catch (Exception e1){
e1.printStackTrace();
}
}
});
fileButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
try{
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setCurrentDirectory(new File("."));
fileChooser.setFileFilter(new FileNameExtensionFilter("Text Files","txt","doc","xls"));
int result = fileChooser.showOpenDialog(f);
if(result==JFileChooser.APPROVE_OPTION)
{
File file = fileChooser.getSelectedFile();
String filename = file.getName();
Long length = file.length()/1024;
String message = "###;file;"+filename+";"+length.toString()+";"+localhost+";2081";
byte[] buffer = message.getBytes();
DatagramPacket packet = new DatagramPacket(buffer,buffer.length,InetAddress.getByName(hostname),1082);
socket1.send(packet);
Socket socket = socket2.accept();
System.out.println("TCP连接成功");
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
if(reader.readLine().equals("yes"))
{
BufferedReader fileIn = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")));
PrintWriter out = new PrintWriter(socket.getOutputStream(),true);
String s;
while((s = fileIn.readLine())!=null)
{
System.out.print(s);
out.println(s);
}
JOptionPane.showMessageDialog(f,"文件传输成功。");
fileIn.close();
out.close();
}
else
{
JOptionPane.showMessageDialog(f,"文件被对方拒收。");
}
reader.close();
socket.close();
}
}catch (Exception e1){
e1.printStackTrace();
}
}
});
}
}
3.Linux服务器代码(ip:192.168.131.135,udp port:1082)
package gui;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class Server
{
public static void main(String[] args) throws Exception
{
String hostname="",text;
DatagramSocket socket = new DatagramSocket(1082);
while(true)
{
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
text = new String(packet.getData(),0,packet.getLength());
int port = packet.getPort();
System.out.print("端口号:"+port+",");
if(port==1080){
System.out.println("来自Win主机:"+text);
port=1081;
hostname="192.168.131.131";//192.168.131.131
}
else if(port==1081){
System.out.println("来自Ubuntu主机:"+text);
port=1080;
hostname="10.177.26.76";
}
else System.out.println("端口号错误");
DatagramPacket packet2 = new DatagramPacket(packet.getData(), packet.getLength(), InetAddress.getByName(hostname),port);
socket.send(packet2);
}
}
}