Java - 通过套接字发送文件(聊天客户端 -> 服务器 -> 另一个聊天客户端)
Posted
技术标签:
【中文标题】Java - 通过套接字发送文件(聊天客户端 -> 服务器 -> 另一个聊天客户端)【英文标题】:Java - send a file over socket(chat client -> server -> another chat client) 【发布时间】:2012-12-15 17:17:20 【问题描述】:所以我正在开发一个聊天程序,现在我想添加一个发送文件选项。 我尝试添加它并且它工作但在文件传输完成后,两个套接字都关闭(两个客户端的套接字)。 这是聊天客户端的 SSCCE:
public class SSCCEChatClient extends JFrame
private JPanel contentPane;
private JTextField inputUsernameField;
private JTextArea textArea;
String serversock = "84.252.37.82";
String username;
Socket sock;
BufferedReader reader;
PrintWriter writer;
InputStreamReader streamreader;
public class IncomingReader implements Runnable
public void run()
String stream;
String[] data;
try
while ((stream = reader.readLine()) != null)
data = stream.split("`");
if(data[2].equals("receiveFile")&&(!data[3].equals(username)))
DataInputStream in = new DataInputStream(sock.getInputStream());
byte[] bytes = new byte[Integer.parseInt(data[1])];
in.read(bytes);
FileOutputStream fos = new FileOutputStream(System.getProperty("user.home") + "\\Desktop\\" + data[0]);
fos.write(bytes);
fos.close();
in.close();
textArea.append("Success!");
else if(data[2].equals("server"))
textArea.append(data[0]);
catch(Exception ex)
//Incoming Reader
public static void main(String[] args)
EventQueue.invokeLater(new Runnable()
public void run()
try
SSCCEChatClient frame = new SSCCEChatClient();
frame.setVisible(true);
catch (Exception e)
e.printStackTrace();
);
public SSCCEChatClient()
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
textArea = new JTextArea();
contentPane.add(textArea, BorderLayout.SOUTH);
JButton btnNewButton = new JButton("Send File");
btnNewButton.addActionListener(new ActionListener()
public void actionPerformed(ActionEvent arg0)
File transferFile = new File (System.getProperty("user.home") + "\\Desktop\\PNG\\Night.png");
byte [] bytearray = new byte [(int)transferFile.length()];
try
BufferedInputStream bin = new BufferedInputStream(new FileInputStream(transferFile));
bin.read(bytearray,0,bytearray.length);
DataOutputStream os = new DataOutputStream(sock.getOutputStream());
writer.println(transferFile.getName() + "`" + transferFile.length() + "`receiveFile`" + username);
writer.flush();
os.write(bytearray,0,bytearray.length);
os.flush();
bin.close();
os.close();
catch (IOException e)
e.printStackTrace();
System.out.println("File transfer complete");
);
contentPane.add(btnNewButton, BorderLayout.CENTER);
JButton btnNewButton_1 = new JButton("Connect");
btnNewButton_1.addActionListener(new ActionListener()
public void actionPerformed(ActionEvent e)
try
username = inputUsernameField.getText();
sock = new Socket(serversock, 5000);
streamreader = new InputStreamReader(sock.getInputStream());
reader = new BufferedReader(streamreader);
writer = new PrintWriter(sock.getOutputStream());
Thread IncomingReader = new Thread(new IncomingReader());
IncomingReader.start();
writer.println(username + "``connect");
writer.flush();
catch (Exception ex)
textArea.append("\nCannot Connect!");
);
contentPane.add(btnNewButton_1, BorderLayout.WEST);
inputUsernameField = new JTextField();
contentPane.add(inputUsernameField, BorderLayout.NORTH);
inputUsernameField.setColumns(10);
这里是服务器端:
public class SSCCEServer
static ArrayList<PrintWriter> clientOutputStreams;
static ArrayList<DataOutputStream> clientDataOutputStreams;
static ArrayList<String> onlineUsers = new ArrayList<>();
public class ClientHandler implements Runnable
BufferedReader reader;
Socket sock;
PrintWriter client;
public ClientHandler(Socket clientSocket, PrintWriter user)
// new inputStreamReader and then add it to a BufferedReader
client = user;
try
sock = clientSocket;
System.out.println(clientSocket.getRemoteSocketAddress().toString() + " - ");
InputStreamReader isReader = new InputStreamReader(sock.getInputStream());
reader = new BufferedReader(isReader);
catch (Exception ex)
System.out.println("error beginning StreamReader");
public void run()
String message;
String[] data;
try
while ((message = reader.readLine()) != null)
data = message.split("`");
if(data[2].equals("receiveFile"))
DataInputStream in = new DataInputStream(sock.getInputStream());
byte[] bytes = new byte[Integer.parseInt(data[1])];
in.read(bytes);
tellEveryone(data[0] + "`" + data[1] + "`" + data[2] + "`" + data[3]);
for(DataOutputStream dos:clientDataOutputStreams)
try
dos.write(bytes);
dos.close();
catch (Exception ex)
System.out.println("error telling everyone");
tellEveryone("File transfer complete``server");
else if(data[2].equals("connect"))
System.out.println(data[0] + "has connected.");
else
System.out.println("No Conditions were met.");
catch (Exception ex)
System.out.println("lost a connection");
System.out.println(ex.getMessage().toString());
clientOutputStreams.remove(client);
public void tellEveryone(String message)
// sends message to everyone connected to server
for(PrintWriter writer:clientOutputStreams)
try
writer.println(message);
//pop("Sending: " + message);
writer.flush();
catch (Exception ex)
System.out.println("error telling everyone");
public static void main(String[] args)
new SSCCEServer().go();
public void go()
clientOutputStreams = new ArrayList<PrintWriter>();
clientDataOutputStreams = new ArrayList<>();
try
@SuppressWarnings("resource")
ServerSocket serverSock = new ServerSocket(5000);
while(true)
Socket clientSock = serverSock.accept();
PrintWriter writer = new PrintWriter(clientSock.getOutputStream());
clientOutputStreams.add(writer);
clientDataOutputStreams.add(new DataOutputStream(clientSock.getOutputStream()));
Thread listener = new Thread(new ClientHandler(clientSock, writer));
listener.start();
catch (Exception ex)
System.out.println("error making a connection");
对不起,如果它真的很长,但这是我可以带它的最小数量。这也不是全部,因为它错过了发送文本方法,但这不会影响 SSCCE。我已经用服务器端的方法“tellEveryone”演示了发送方法。 此外,“\PNG\Night.png”只是一个示例,您可以创建自己的文件夹和文件以运行 SSCCE。 文件发送后如何解决套接字关闭问题?
【问题讨论】:
codereview.SE 中询问了如何让我的代码变得更好。关于套接字关闭,您重复了一个更简洁的问题***.com/questions/2826311/… 【参考方案1】:关闭finally
块中的所有Objects
(try - catch - finally
)
Concurency in Swing 有问题,但有三种方法
a) 正确的方法
将代码包装到Runnable#Thread
(最简单),必须将对Swing GUI 的任何更改包装到invokeLater()
使用SwingWorker
(以标准方式实现),其中方法publish
、process
和done
相当保证所有events
都在EDT
上完成
b) 捷径,有效但不正确
直接将 Swing GUI 代码封装到invokeLater()
中
【讨论】:
【参考方案2】:当您关闭输出流时,套接字会关闭。如果要保持套接字打开,请不要关闭流。参考看看SocketOutputStream.java
【讨论】:
以上是关于Java - 通过套接字发送文件(聊天客户端 -> 服务器 -> 另一个聊天客户端)的主要内容,如果未能解决你的问题,请参考以下文章