当前位置: 移动技术网 > IT编程>开发语言>Java > Java基于TCP方式的二进制文件传输

Java基于TCP方式的二进制文件传输

2019年07月22日  | 移动技术网IT编程  | 我要评论

芊孜,迷失的圣杯,打印机驱动装不上

一个基于java socket协议之上文件传输的完整示例,基于tcp通信完成。

除了基于tcp的二进制文件传输,还演示了java swing的一些编程技巧,demo程序

实现主要功能有以下几点:

  • 1.基于java socket的二进制文件传输(包括图片,二进制文件,各种文档work,pdf)
  • 2.swingworker集合jprogressbar显示实时传输/接受完成的百分比
  • 3.其它一些swing多线程编程技巧

首先来看一下整个dome的class之间的关系图:

下面按照上图来详细解释各个类的功能与代码实现:

服务器端:

filetransferserver类的功能首先是在端口9999创建一个服务器套接字并

开始监听连接。相关代码如下:

private void startserver(int port) { 
 try { 
  serversocket = new serversocket(port); 
  system.out.println("server started at port :" + port); 
  while(true) { 
   socket client = serversocket.accept(); // blocked & waiting for income socket 
   system.out.println("just connected to " + client.getremotesocketaddress()); 
   filereceivetask task = new filereceivetask(client); 
   bar.setvalue(0); // reset it now 
   task.addpropertychangelistener(new propertychangelistener() { 
    public void propertychange(propertychangeevent evt) { 
     if ("progress".equals(evt.getpropertyname())) { 
      bar.setvalue((integer) evt.getnewvalue()); 
     } 
    } 
   }); 
    
   task.execute(); 
  } 
 
 } catch (ioexception e) { 
  e.printstacktrace(); 
 } 
} 

关于propertychangelistener, java提供了一个非常有力的工具类来
监控任意bean model的数据改变,程序通过添加该监听器实现对

swingworker的progress属性值改变的事件捕获,然后更新jprogressbar

实例对象,实现了ui的刷新。filetransferserver类的完整源代码如下:

package com.gloomyfish.socket.tutorial.filetransfer; 
 
import java.awt.borderlayout; 
import java.awt.flowlayout; 
import java.awt.event.actionevent; 
import java.awt.event.actionlistener; 
import java.beans.propertychangeevent; 
import java.beans.propertychangelistener; 
import java.io.ioexception; 
import java.net.serversocket; 
import java.net.socket; 
 
import javax.swing.boxlayout; 
import javax.swing.jbutton; 
import javax.swing.jframe; 
import javax.swing.joptionpane; 
import javax.swing.jpanel; 
import javax.swing.jprogressbar; 
 
public class filetransferserver extends jframe implements actionlistener { 
 /** 
  * 
  */ 
 public final static string start_svr = "start"; 
 public final static string shut_down_svr = "shut down"; 
 public final static string end_flag = "eof"; 
 private static final long serialversionuid = 1l; 
 private serversocket serversocket; 
 private jbutton startbtn; 
 private jprogressbar bar; 
 public filetransferserver() { 
  super("file server"); 
  initcomponent(); 
  setuplistener(); 
 } 
 
 private void setuplistener() { 
  startbtn.addactionlistener(this); 
 } 
 
 private void initcomponent() { 
  startbtn = new jbutton(start_svr); 
  jpanel progresspanel = new jpanel(); 
  progresspanel.setlayout(new boxlayout(progresspanel, boxlayout.y_axis)); 
  bar = new jprogressbar(); 
  bar.setminimum(0); 
  bar.setmaximum(100); 
  progresspanel.add(bar); 
  getcontentpane().setlayout(new borderlayout()); 
  jpanel btnpanel = new jpanel(new flowlayout(flowlayout.right)); 
  btnpanel.add(startbtn); 
  getcontentpane().add(btnpanel, borderlayout.south); 
  getcontentpane().add(progresspanel, borderlayout.center); 
 } 
  
 private void startserver(int port) { 
  try { 
   serversocket = new serversocket(port); 
   system.out.println("server started at port :" + port); 
   while(true) { 
    socket client = serversocket.accept(); // blocked & waiting for income socket 
    system.out.println("just connected to " + client.getremotesocketaddress()); 
    filereceivetask task = new filereceivetask(client); 
    bar.setvalue(0); // reset it now 
    task.addpropertychangelistener(new propertychangelistener() { 
     public void propertychange(propertychangeevent evt) { 
      if ("progress".equals(evt.getpropertyname())) { 
       bar.setvalue((integer) evt.getnewvalue()); 
      } 
     } 
    }); 
     
    task.execute(); 
   } 
 
  } catch (ioexception e) { 
   e.printstacktrace(); 
  } 
 } 
  
 public void showsuccess() { 
  bar.setvalue(100); 
  joptionpane.showmessagedialog(this, "file received successfully!"); 
 } 
 
 @override 
 public void actionperformed(actionevent e) { 
  if(start_svr.equals(e.getactioncommand())) { 
   thread startthread = new thread(new runnable() { 
    public void run() { 
     startserver(9999); 
    } 
   }); 
   startthread.start(); 
   startbtn.setenabled(false); 
  } else if(shut_down_svr.equals(e.getactioncommand())) { 
 
  } else { 
   // do nothing... 
  } 
 } 
  
 public static void main(string[] args) { 
  filetransferserver server = new filetransferserver(); 
  server.setdefaultcloseoperation(jframe.exit_on_close); 
  server.setsize(400, 400); 
  server.setresizable(false); 
  server.setvisible(true); 
 } 
} 

filereceivetask是服务器端的文件接受类:
首先从建立的tcp流中得到文件名与文件大小,然后开始接受文件内容字节

并写入创建的文件对象流中,最后验证文件大小与写入的字节流是否相等

最后发送一条消息到文件发送方,告诉对方文件传输完成,可以关闭tcp流。

该类的完整源代码如下:

package com.gloomyfish.socket.tutorial.filetransfer; 
 
import java.io.bufferedoutputstream; 
import java.io.bufferedwriter; 
import java.io.datainputstream; 
import java.io.file; 
import java.io.fileoutputstream; 
import java.io.outputstreamwriter; 
import java.net.socket; 
 
import javax.swing.swingworker; 
 
public class filereceivetask extends swingworker<integer, object> { 
 private socket _msocket; 
 public filereceivetask(socket client) { 
  this._msocket = client; 
 } 
 
 @override 
 protected integer doinbackground() throws exception { 
  // get file meta information 
  datainputstream input = new datainputstream(_msocket.getinputstream()); 
  string filename = input.readutf(); 
  int filelength = (int)input.readlong(); // number of total bytes 
  file file = new file("c:\\users\\fish\\downloads" + file.separator + filename); 
  bufferedoutputstream output = new bufferedoutputstream(new fileoutputstream(file)); 
  system.out.println("received file name = " + filename); 
  system.out.println("received file size = " + filelength/1024 + "kb"); 
   
  // start to receive the content of the file and write them 
  byte[] content = new byte[2048]; 
  int offset = 0; 
  int numreadbytes = 0; 
  while(offset < filelength && (numreadbytes = input.read(content)) > 0) { 
   output.write(content, 0, numreadbytes); 
   float precent = 100.0f * ((float)offset)/((float)filelength); 
   setprogress((int)precent); 
   offset += numreadbytes; 
  } 
  system.out.println("numreadbytes = " + numreadbytes); 
  if(offset < filelength) { 
   numreadbytes = input.read(content); 
   system.out.println("numreadbytes = " + numreadbytes); 
   system.out.println("file content error at server side"); 
  } else { 
   system.out.println("file receive task has done correctly"); 
  } 
  setprogress(100); 
   
  // tell client to close the socket now, we already receive the file successfully!! 
  bufferedwriter bufferedwriter = new bufferedwriter(new outputstreamwriter(_msocket.getoutputstream())); 
  bufferedwriter.write("done\r\n"); 
  bufferedwriter.flush(); 
   
  // close the file and socket 
  output.close(); 
  _msocket.close(); 
  return 100; 
 } 
 
} 

客户端:
filetransferclient是客户端ui类,用来实现到服务端的连接,然后选择

要传输的文件(图片,pdf,word文档等各种二进制文件)。如果没有

输入服务器信息,会弹出提示要求输入。端口已经指定为:9999

【send file】按钮会打开文件选择框,用户选择要传输文件以后,创建

filetransfertask线程,并开始执行文件传送。客户端ui代码如下:

package com.gloomyfish.socket.tutorial.filetransfer; 
 
import java.awt.borderlayout; 
import java.awt.flowlayout; 
import java.awt.gridlayout; 
import java.awt.event.actionevent; 
import java.awt.event.actionlistener; 
import java.beans.propertychangeevent; 
import java.beans.propertychangelistener; 
import java.io.file; 
import java.net.inetsocketaddress; 
import java.net.socketaddress; 
 
import javax.swing.borderfactory; 
import javax.swing.boxlayout; 
import javax.swing.jbutton; 
import javax.swing.jfilechooser; 
import javax.swing.jframe; 
import javax.swing.jlabel; 
import javax.swing.joptionpane; 
import javax.swing.jpanel; 
import javax.swing.jprogressbar; 
import javax.swing.jtextfield; 
/** 
 * 我一般写英文注释,偶尔我也会写中文注释,只是觉得写英文 
 * 注释跟代码比较统一,无他。 
 */ 
public class filetransferclient extends jframe implements actionlistener { 
 /** 
  * 
  */ 
 private static final long serialversionuid = 1l; 
 public final static string send_cmd = "send file"; 
 public final static int minimum = 0; 
 public final static int maximum = 100; 
 // public final static string connect_cmd = "connect"; 
 private jbutton sendfilebtn; 
 private jtextfield serverfield; 
 private jtextfield portfield; 
 private jprogressbar bar; 
  
 public filetransferclient() { 
  super("file transfer client"); 
  initcomponents(); 
 } 
 
 private void initcomponents() { 
  getcontentpane().setlayout(new borderlayout()); 
  jpanel progresspanel = new jpanel(); 
  progresspanel.setlayout(new boxlayout(progresspanel, boxlayout.y_axis)); 
  bar = new jprogressbar(); 
  progresspanel.add(bar); 
  bar.setminimum(minimum); 
  bar.setmaximum(maximum); 
  jpanel serversettingpanel = new jpanel(); 
  serversettingpanel.setlayout(new gridlayout(2,2,5,5)); 
  serversettingpanel.setborder(borderfactory.createtitledborder("server setting")); 
  serverfield = new jtextfield(); 
  portfield = new jtextfield(); 
  serversettingpanel.add(new jlabel("server ip/host:")); 
  serversettingpanel.add(serverfield); 
  serversettingpanel.add(new jlabel("server port:")); 
  serversettingpanel.add(portfield); 
   
  sendfilebtn = new jbutton(send_cmd); 
  jpanel btnpanel = new jpanel(); 
  btnpanel.setlayout(new flowlayout(flowlayout.right)); 
  btnpanel.add(sendfilebtn); 
  getcontentpane().add(serversettingpanel, borderlayout.north); 
  getcontentpane().add(btnpanel, borderlayout.south); 
  getcontentpane().add(progresspanel, borderlayout.center); 
  sendfilebtn.addactionlistener(this); 
 } 
 
 @override 
 public void actionperformed(actionevent e) { 
  string command = e.getactioncommand(); 
  if(command.equals(send_cmd)) { 
   if(checknull()) { 
    joptionpane.showmessagedialog(this, "please enter server host and port in order to set up the connection!"); 
    return; 
   } 
   jfilechooser chooser = new jfilechooser(); 
   int status = chooser.showopendialog(null); 
   if (status == jfilechooser.approve_option) { 
    file f = chooser.getselectedfile(); 
    socketaddress address = new inetsocketaddress(getserver(), getport()); 
    filetransfertask task = new filetransfertask(f, address, this); 
    bar.setvalue(0); 
    task.addpropertychangelistener(new propertychangelistener() { 
     public void propertychange(propertychangeevent evt) { 
      if ("progress".equals(evt.getpropertyname())) { 
       bar.setvalue((integer) evt.getnewvalue()); 
      } 
     } 
    }); 
    task.execute(); // 异步task执行 
   } 
  } else { 
   // do nothing 
  } 
 } 
  
 public void showsuccess() { 
  bar.setvalue(100); 
  joptionpane.showmessagedialog(this, "file send successfully!"); 
 } 
  
 public string getserver() { 
  return serverfield.gettext().trim(); 
 } 
  
 public int getport() { 
  return integer.parseint(portfield.gettext().trim()); 
 } 
 /** 
  * make sure the ui already have some correct input information here!!! 
  * @return 
  */ 
 private boolean checknull() { 
  string servername = serverfield.gettext(); 
  string port = portfield.gettext(); 
  if(servername == null || servername.length() == 0 || port == null || port.length() == 0) { 
   return true; 
  } 
   
  try { 
   integer.parseint(port); // try to parse it as server port number , validation code. 
  } catch(numberformatexception ne) { 
   ne.printstacktrace(); 
   return true; 
  } 
  return false; 
 } 
  
 public static void main(string[] args) { 
  filetransferclient client = new filetransferclient(); 
  client.setdefaultcloseoperation(jframe.exit_on_close); 
  client.setsize(400, 400); 
  client.setresizable(false); 
  // client.pack(); 
  client.setvisible(true); 
 } 
 
} 

filetransfertask实现的功能主要有:

  • 1. 发送文件meta信息到接受方(文件名与文件大小)
  • 2. 读取文件内容字节写入socket字节流中,发送到接受方
  • 3. 从socket字节流中读取对方接受完成通知信息,调用弹出文件传输成功信息

该类完全源代码如下:

package com.gloomyfish.socket.tutorial.filetransfer; 
 
import java.io.bufferedinputstream; 
import java.io.bufferedreader; 
import java.io.datainputstream; 
import java.io.dataoutputstream; 
import java.io.file; 
import java.io.fileinputstream; 
import java.io.ioexception; 
import java.io.inputstreamreader; 
import java.net.socket; 
import java.net.socketaddress; 
 
import javax.swing.swingworker; 
 
public class filetransfertask extends swingworker<integer, object> { 
 private file selectedfile; 
 private socket msocket; 
 private socketaddress address; 
 private filetransferclient parent; 
  
 public filetransfertask(file file, socketaddress address, filetransferclient owner /*, jprogressbar progress*/) { 
  this.address = address; 
  this.selectedfile = file; 
  msocket = new socket(); 
  this.parent = owner; 
 } 
  
 @override 
 protected integer doinbackground() throws exception { 
  // get the size of the file 
  long length = selectedfile.length(); 
  if (length > integer.max_value) { 
   throw new ioexception("could not completely read file " + selectedfile.getname() + " as it is too long (" + length + " bytes, max supported " + integer.max_value + ")"); 
  } 
   
  msocket.connect(address); 
   
  // create the byte array to hold the file data 
  msocket.setsolinger(true, 60); 
  dataoutputstream dout = new dataoutputstream(msocket.getoutputstream()); 
  // now we start to send the file meta info. 
  dout.writeutf(selectedfile.getname()); 
  dout.writelong(length); 
  dout.flush(); 
  // end comment 
  filedatapackage pdata = new filedatapackage(); 
  datainputstream is = new datainputstream(new fileinputstream(selectedfile)); 
  byte[] bytes = new byte[2048]; 
 
  // read in the bytes 
  int offset = 0; 
  int numread = 0; 
  int fsize = (int)length; 
  while (offset < fsize && (numread=is.read(bytes, 0, bytes.length)) >= 0) { 
   pdata.setdata(bytes, numread); 
   dout.write(pdata.getpackagedata(), 0, pdata.getpackagedata().length); 
   dout.flush(); 
   offset += numread; 
   float precent = 100.0f * ((float)offset)/((float)fsize); 
   setprogress((int)precent); 
  } 
  system.out.println("total send bytes = " + offset); 
  // ensure all the bytes have been read in 
  if (offset < fsize) { 
   throw new ioexception("could not completely transfer file " + selectedfile.getname()); 
  } 
  msocket.shutdownoutput(); 
   
  // receive the file transfer successfully message from connection 
   
  bufferedinputstream streamreader = new bufferedinputstream(msocket.getinputstream()); 
  bufferedreader bufferedreader = new bufferedreader(new inputstreamreader(streamreader)); 
  string donemsg = bufferedreader.readline(); 
  if("done".equals(donemsg)) { 
   parent.showsuccess(); 
  } 
  // close the file input stream 
  setprogress(100); 
  // dout.close(); 
  msocket.close(); 
  is.close(); 
  system.out.println("close it now......"); 
  return 100; 
 } 
} 

数据包类如下,不解释!

package com.gloomyfish.socket.tutorial.filetransfer; 
/** 
 * this is very simple file transfer protocol over tcp socket 
 */ 
public class filedatapackage { 
 
 private int datalength; // 数据包中数据长度,两个字节 
 private byte[] databuff; // 数据包中数据,meici最大不超过2048字节 
  
 public final static byte[] eof = new byte[]{'e', 'o','f'}; 
  
 public filedatapackage() { 
  datalength = 0; 
  databuff = new byte[2048]; 
 } 
  
 public byte[] getpackagedata() { 
  byte[] pdata = new byte[datalength]; 
  // end comment 
  system.arraycopy(databuff, 0, pdata, 0, datalength); 
  return pdata; 
 } 
  
 public void setdata(byte[] data, int bsize) { 
  datalength = bsize; 
  for(int i=0; i<databuff.length; i++) { 
   if(i<bsize) { 
    databuff[i] = data[i]; 
   } else { 
    databuff[i] = ' '; 
   } 
  } 
 } 
} 

每次发送的最大字节数为2048个字节。程序最终运行效果如下(win7 + jdk6u30):

以上就是本文的全部内容,希望对大家的学习有所帮助。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网