实现端到端的文件传输
聊天器第三版–java 提示:客户发送端读取本地文件字节流,通过套接字将其发送至服务器,服务器将文件字节流转发至客户接收端,客户接收端从套接字中读取字节流,写入到本地文件。
与3不同的是增加了一个文件的传输,不同之处在于客户端,增加了一个按钮,文件的路径需要从textfield处输入,当点击file按钮时,开始文件传输。
客户端:
package chat4;
import java.net.*;
import java.awt.Container;
import java.io.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.*;
public class chatClient extends JFrame implements Runnable,ActionListener{
private static final long serialVersionUID = -7103711638195532808L;
DataInputStream in;
DataOutputStream out;
Socket socket;//保留与本线程相关的对象
String name;
String prefix;//读取到文件的后缀名
JTextField textField;//输入区
JTextArea textArea;//显示区
JButton button;
JMenuItem open;
JButton button1;
//构造函数
public chatClient(String name)throws IOException{
this.name=name;
this.setTitle("我是 "+name);
init();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(null);
this.setBounds(0, 0, 300, 530);
this.setResizable(false);
this.setLocation(400, 200);
this.setVisible(true);
try {
//向本机的2345端口发出客户请求
this.socket=new Socket("127.0.0.1",2345);
//由Socket对象得到输入流
in=new DataInputStream(socket.getInputStream());
//由Socket对象得到输出流
out=new DataOutputStream(socket.getOutputStream());
} catch (IOException e) {}
}
//线程启动,显示输入区
@Override
public void run() {
try{
while(true){
String ss=in.readUTF();
textArea.append(ss+"\n");
out.flush();
}
}catch(IOException e){
e.printStackTrace();
}
}
//swing界面构造
void init(){
Container con = getContentPane();
button1=new JButton("file");
button1.setBounds(0,0,285,30);
button1.addActionListener(this);
con.add(button1);
button=new JButton("Send");
button.setBounds(225,450,65,20);
button.addActionListener(this);
con.add(button);
textField=new JTextField();
textField.setBounds(5,450,215,20);
textField.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_ENTER){
try {
out.writeUTF(textField.getText());
textField.setText("");
}catch(IOException e2) {}
}}});
con.add(textField);
textArea=new JTextArea();
textArea.setBounds(5, 35, 285,440);
textArea.setEditable(false);
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setBorder(null);
scrollPane.setBounds(5, 35, 285,440);
con.add(scrollPane);
}
@Override
//AWT事件处理
public void actionPerformed(ActionEvent e) {
if(e.getSource() == button) {
try {
out.writeUTF(this.name+": "+textField.getText());
out.flush();
textField.setText("");
} catch (IOException e1) {
e1.printStackTrace();
}
}
//当button1按钮响应,如果文件路径有效,就进行文件传输
else if(e.getSource()== button1)
{
try {
out.writeUTF(this.name+": "+"文件传输开始: "+textField.getText());
String prefix = textField.getText();
textField.setText("");
File file = new File(prefix);
//读文件,并且刷入流中
String readline;
System.out.println("file");
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(file));
} catch (FileNotFoundException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
try {
while((readline=in.readLine())!=null)
{
textField.setText(readline);
out.writeUTF(textField.getText());
out.flush();//刷新输出流,使Server马上收到该字符串
}
textField.setText("");
out.writeUTF("文件传输结束!");
} catch (IOException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
try {
in.close();
} catch (IOException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
public static void main(String[] args)throws IOException {
String []name = {"一","二","三","四","五","六","七","八","九","十"};
java.util.Random random=new java.util.Random();
int result=random.nextInt(10);
new Thread(new chatClient(name[result])).start();
}
}
最开始进行文件传输时直接用System.out.println()函数输出了,但是这样的结果并不能显示在swing,并且只能在本客户端显示,并不能传输到其他客户端。后来突然间想到将文件中的数据先显示在textfield上,通过多线程在显示区显示数据以使得客户端与客户端间可以进行文件传输。
结果图展示:
其中:传输的两个文件来自我的桌面
5.聊天室私聊和基于端与端之间的文件传输:
刚开始的时候,由于理解不到位,所以写成了聊天器的群聊,现改正如下聊天室的私聊(无界面版)。
客户端
package chat5;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
Socket socket;//保留与本线程相关的对象
private static final int MESSAGE_SIZE = 1024;//每次允许接受数据的最大长度
private Scanner scanner;
public static void main(String[] args) {
new Client();
}
public Client() {
try {
//向本机的45678端口发送客户请求
socket = new Socket("127.0.0.1", 45678);
if (socket.isConnected() == true) {
System.out.println("连接成功");
new Thread() {//开启一个接受数据的线程
@Override
public void run() {
super.run();
InputStream in;
try {
in = socket.getInputStream();
byte[] b;
while (true) {
b = new byte[MESSAGE_SIZE];
in.read(b);
System.out.println("接收到服务端消息:" + new String(b));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
OutputStream out = null;
while (true) {
scanner = new Scanner(System.in);
String str = scanner.nextLine();
out = socket.getOutputStream();
out.write(str.getBytes());
out.flush();
if (str.equals("end")) {
System.exit(0);//关闭客户端
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务器端:
package chat5;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class Server {
List<ReceiveThread> receiveList = new ArrayList<>();//存放已连接客户端类
private final static int MESSAGE_SIZE = 1024;//每次允许接受数据的最大长度
int num = 0;//客户端编号
public static void main(String[] args) {
new Server();
}
//服务端处理逻辑
Server() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(45678);//用来监听的套接字,指定端口号
while (true) {
Socket socket = serverSocket.accept();//监听客户端连接,阻塞线程
System.out.println("连接上用户:" + num);
//在其他线程处理接收来自客户端的消息
ReceiveThread receiveThread = new ReceiveThread(socket, num);
receiveThread.start();
receiveList.add(receiveThread);
//有客户端新上线,服务器就通知其他客户端
String notice="有新用户上线,现在在线用户有:用户:";
for (ReceiveThread thread : receiveList) {
notice = notice + "" + thread.num;
}
for (ReceiveThread thread : receiveList) {
new SendThread(thread.socket, notice).start();
}
num++;
}
} catch (IOException e) {
e.printStackTrace();
}
}
//接受消息的线程(同时也有记录对应客户端socket的作用)
class ReceiveThread extends Thread {
int num;
Socket socket;//客户端对应的套接字
boolean continueReceive = true;//标识是否还维持连接需要接收
public ReceiveThread(Socket socket, int num) {
this.socket = socket;
this.num = num;
try {
//给连接上的客户端发送,分配的客户端编号的通知
socket.getOutputStream().write(("你的用户编号是" + num).getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
super.run();
//接收客户端发送的消息
InputStream inputStream = null;
try {
inputStream = socket.getInputStream();
byte[] b;
while (continueReceive) {
b = new byte[MESSAGE_SIZE];
inputStream.read(b);
b = splitByte(b);//去掉数组无用部分
//发送end的客户端断开连接
if (new String(b).equals("end")) {
continueReceive = false;
receiveList.remove(this);
//通知其他客户端
String message = "用户" + num + "连接断开\n" +
"现在在线的有,用户:";
for (ReceiveThread receiveThread : receiveList) {
message = message + " " + receiveThread.num;
}
System.out.println(message);
for (ReceiveThread receiveThread : receiveList) {
new SendThread(receiveThread.socket, message).start();
}
} else {
try {
String[] data = new String(b).split(" ", 2);//以第一个空格,将字符串分成两个部分
int clientNum = Integer.parseInt(data[0]);//转换为数字,即客户端编号数字
//将消息发送给指定客户端
for (ReceiveThread receiveThread : receiveList) {
if (receiveThread.num == clientNum) {
new SendThread(receiveThread.socket, "用户"+num+"发消息:"+data[1]).start();
System.out.println("用户" + num + "用户" + receiveThread.num + ": " + data[1]);
//如果输入的字符串为file的话,就继续输入一个字符串file1,即文件路径
//然后按行的形式,将文件传输给私聊用户
if(data[1].equals("file"))
{
System.out.println("用户" + num + "用户" + receiveThread.num + ": " + data[1]);
byte[] a;
a = new byte[MESSAGE_SIZE];
inputStream.read(a);
a = splitByte(a);//去掉数组无用部分
String file1= new String (a);
new SendThread(receiveThread.socket, "用户"+num+"发消息:"+file1).start();
File file = new File(file1);
String readline;
BufferedReader in = null;
in = new BufferedReader(new FileReader(file));
while((readline=in.readLine())!=null)
{
new SendThread(receiveThread.socket, "用户"+num+"发消息:"+readline).start();
System.out.println("用户" + num + "用户" + receiveThread.num + ": " + readline);
}
in.close();
}
//如果输入的字符串非file,则直接将data[1]给私聊用户
// else {
// System.out.println("用户" + num + "用户" + receiveThread.num + ": " + data[1]);}
//}
}
}} catch (Exception e) {
new SendThread(socket, "输入错误,请重新输入").start();
System.out.println("客户端输入错误");
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {//关闭资源
if (inputStream != null) {
inputStream.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//发送消息的线程
class SendThread extends Thread {
Socket socket;
String str;
public SendThread(Socket socket, String str) {
this.socket = socket;
this.str = str;
}
@Override
public void run() {
super.run();
try {
socket.getOutputStream().write(str.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
//去除byte数组多余部分
private byte[] splitByte(byte b[]) {
int i = 0;
for(;i<b.length;i++) {
if (b[i] == 0) {
break;
}
}
byte[] b2 = new byte[i];
for (int j = 0; j <i ; j++) {
b2[j] = b[j];
}
return b2;
}
}
结果图展示: