`
yangsq
  • 浏览: 180817 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

用java实现的简单Server/Client文件传输

阅读更多

老师留作业了,是一道网络编程题目,socket知识在先,requirement如下:

  • 用一种编程语言实现一个简单的Server/Client程序;
  • 该程序的主要功能是利用Client从Server端下载一个文件;
  • 在下载之前,需要有一定的用户身份验证机制(说白了就是先输入以下用户名和密码);
  • Server应该是多线程机制的,即为每个Client请求Server都要有一个线程去处理,而不是所有的Client都是一个Server线程处理。

ok,这就是需求,单从编程角度来讲,题目不难,单老师说过一句话,我觉得非常有道理,“看一万个程序,不如自己亲自写一个程序,写一万个程序,不如努力写一个好程序”,所以我就利用十一假期的最后两天,完成了这样一个作业,当然大部分时间还是画在了学习不太熟悉的python上。在这里,还要感谢一下CSDN上“wumingabc的专栏”的一篇blog,参考了他的一些代码。

废话少说,上code(这篇是java版,python版):

处理流程:

  1. server启动,监听client请求;
  2. client启动;
  3. server监听到client,发送“Hi”;
  4. client收到“Hi”
  5. client要求用户输入用户名;
  6. 用户输入用户名(如yangsq),发送到服务器(形式为“username:yangsq”);
  7. 服务器验证是否存在这样一个用户,如果有到step 8,否则转到5;
  8. client用求用户输入密码(如123456),发送到服务器(形式为“password:123456”);
  9. 服务器验证密码是否正确,不正确转到step 8,正确开始传输文件(为了简单,文件预先指定好);
  10. client收到文件,结束后发送“bye”;同时server端收到“bye”后结束线程。

服务器端:

java 代码
  1. import java.io.BufferedOutputStream;   
  2. import java.io.BufferedReader;   
  3. import java.io.BufferedWriter;   
  4. import java.io.DataOutputStream;   
  5. import java.io.File;   
  6. import java.io.FileInputStream;   
  7. import java.io.IOException;   
  8. import java.io.InputStream;   
  9. import java.io.InputStreamReader;   
  10. import java.io.OutputStream;   
  11. import java.io.OutputStreamWriter;   
  12. import java.io.PrintWriter;   
  13. import java.net.ServerSocket;   
  14. import java.net.Socket;   
  15. import java.util.HashMap;   
  16. import java.util.Map;   
  17.   
  18. public class SimpleServer extends Thread{   
  19.     private static final int DEFAULT_PORT = 4444;   
  20.     private static final String DEFAULT_FILE_NAME = "PyNet.pdf";   
  21.     private Socket socket;   
  22.        
  23.     public SimpleServer(Socket socket){   
  24.         this.socket = socket;   
  25.         start();   
  26.     }   
  27.        
  28.     @Override  
  29.     public void run() {   
  30.         System.out.println("Connected from " + socket.getRemoteSocketAddress());   
  31.         try {   
  32.             BufferedReader in = new BufferedReader(   
  33.                     new InputStreamReader(socket.getInputStream()));   
  34.             PrintWriter out = new PrintWriter(   
  35.                     new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);   
  36.             String inputLine, outputLine;   
  37.                
  38.             HandleInput handleInput = new HandleInput();   
  39.             outputLine = handleInput.handle(null);   
  40.             out.println(outputLine);   
  41.                
  42.             while((inputLine = in.readLine()) != null){   
  43.                 outputLine = handleInput.handle(inputLine);   
  44.                 out.println(outputLine);   
  45.                 out.flush();   
  46.                 if(outputLine.equals("bye"))   
  47.                     break;   
  48.                 if(outputLine.equals("password:valid")){   
  49.                     //prepare for the transmission of the file   
  50.                     Thread.sleep(2000);   
  51.                     InputStream fileInput = new FileInputStream(new File(DEFAULT_FILE_NAME));   
  52.                     OutputStream fileOutput = new DataOutputStream(   
  53.                             new BufferedOutputStream(socket.getOutputStream()));   
  54.                     byte[] buf = new byte[2048];   
  55.                        
  56.                     //transmit the file   
  57.                     int num = fileInput.read(buf);   
  58.                     while(num != -1){   
  59.                         fileOutput.write(buf, 0, num);   
  60.                         fileOutput.flush();   
  61.                         num = fileInput.read(buf);   
  62.                     }   
  63.                        
  64.                     fileInput.close();   
  65.                     fileOutput.close();   
  66.                 }   
  67.             }   
  68.                
  69.             in.close();   
  70.             out.close();   
  71.             System.out.println("Disconnected from " + socket.getRemoteSocketAddress());   
  72.             socket.close();   
  73.         } catch (IOException e) {   
  74.             // TODO Auto-generated catch block   
  75.             e.printStackTrace();   
  76.         } catch (InterruptedException e) {   
  77.             // TODO Auto-generated catch block   
  78.             e.printStackTrace();   
  79.         }   
  80.     }   
  81.        
  82.     private class HandleInput{   
  83.         private  Map userInfo = new HashMap();   
  84.         private String username = "";   
  85.         private String password = "";   
  86.            
  87.         public HandleInput(){   
  88.             userInfo.put("yangsq""yangsq");   
  89.             userInfo.put("abc""abc");   
  90.             userInfo.put("123""123");   
  91.         }   
  92.            
  93.         public String handle(String input){   
  94.             String output = "";   
  95.             if(input == null)   
  96.                 output = "Hi";   
  97.             else if(input.startsWith("username")){   
  98.                 username = input.split(":")[1];   
  99.                 if(userInfo.containsKey(username))   
  100.                     output = "username:valid";   
  101.                 else  
  102.                     output = "username:invalid";   
  103.             }else if(input.startsWith("password")){   
  104.                 password = input.split(":")[1];   
  105.                 if(userInfo.get(username).equals(password))   
  106.                     output = "password:valid";   
  107.                 else  
  108.                     output = "password:invalid";   
  109.             }else if(input.equals("bye")){   
  110.                 output = "bye";   
  111.             }   
  112.             return output;   
  113.         }   
  114.     }   
  115.   
  116.     public static void main(String[] args) {   
  117.            
  118.         int port = DEFAULT_PORT;   
  119.            
  120.         if(args.length > 0){   
  121.             port = Integer.parseInt(args[0]);   
  122.         }   
  123.            
  124.         try {   
  125.             ServerSocket serverSocket = new ServerSocket(port);   
  126.             System.out.println("Server Started");   
  127.             try {   
  128.                 while(true){   
  129.                     Socket theSocket = serverSocket.accept();   
  130.                     try {   
  131.                         new SimpleServer(theSocket);   
  132.                     } catch (Exception e) {   
  133.                         e.printStackTrace();   
  134.                         theSocket.close();   
  135.                     }   
  136.                 }   
  137.             } catch (Exception e) {   
  138.                 // TODO: handle exception   
  139.                 e.printStackTrace();   
  140.             } finally {   
  141.                 if(serverSocket != null)   
  142.                     serverSocket.close();   
  143.             }   
  144.         } catch (IOException e) {   
  145.             // TODO Auto-generated catch block   
  146.             e.printStackTrace();   
  147.         }   
  148.     }   
  149. }  

说明:

  • main函数是Server的启动点,在这里我们建立了一个ServerSocket的实例,这个类是java中专门进行Server端编程的,它可以进行很多的复杂配置,这里知识把它建立在了一个端口之上(line-125),然后为请求返回socket(line-131)。
  • SimpleServer类继承了Thread,也就是说,我的想法是为每一个Client请求,都有一个SimpleServer去处理。这是怎样实现的呢?看line-128到line-136,这里ServerSocket的实例在端口4444进行监听,只要有一个请求,就new一个SimpleServer去处理(如果没有请求,程序就会阻塞在ServerSocket的accept方法上line-129)
  • 既然SimpleServer继承了Thread,那么它的最重要的方法就是run(line-29),可以看到,在new SimpleServer的时候就启动了它的run方法。
  • SimpleServer的主要处理流程在line-42到line-67,为了更清晰,把其中的用户身份验证提出来组成一个内部类HandleInput。
  • 负责文件传输的是line-48到line-66,也即clien的密码正确后开始。这里需要说明的是流的实现,与用户交互的时候我们用的是New IO,他们是面向字节(1字节=2byte)的,这很合适,因为我们的用户信息都是字节的(简单的说就是面向asc字符的),所以这里我们就用了readline和println方法(这两个都是Reader和Writer的方法);但是我们要传输的是一个二进制文件(说白了就是面向byte的),所以用New IO就不合适了,所以转向了InputStream(读入文件)和OutputStream(把文件通过socket写出)。

客户端:

java 代码
  1. import java.io.BufferedInputStream;   
  2. import java.io.BufferedReader;   
  3. import java.io.DataInputStream;   
  4. import java.io.File;   
  5. import java.io.IOException;   
  6. import java.io.InputStream;   
  7. import java.io.InputStreamReader;   
  8. import java.io.PrintWriter;   
  9. import java.io.RandomAccessFile;   
  10. import java.net.Socket;   
  11. import java.net.UnknownHostException;   
  12.   
  13. public class SimpleClient {   
  14.     private static final int DEFAULT_PORT = 4444;   
  15.     private static final String DEFAULT_HOST = "localhost";   
  16.        
  17.     public static void main(String[] args) {   
  18.         String host = DEFAULT_HOST;   
  19.         int port = DEFAULT_PORT;   
  20.            
  21.         if(args.length > 0){   
  22.             host = args[0];   
  23.         }   
  24.            
  25.         if(args.length >1 ){   
  26.             port = Integer.parseInt(args[1]);   
  27.         }   
  28.            
  29.         Socket theSocket = null;   
  30.         PrintWriter out = null;   
  31.         BufferedReader in = null, userIn = null;   
  32.            
  33.         try {   
  34.             theSocket = new Socket(host, port);   
  35.                
  36.             in = new BufferedReader(   
  37.                     new InputStreamReader(theSocket.getInputStream()));   
  38.             out = new PrintWriter(theSocket.getOutputStream());   
  39.             userIn = new BufferedReader(   
  40.                     new InputStreamReader(System.in));   
  41.                
  42.             System.out.println("Connected to the simple file server");   
  43.             String fromUser, fromServer;   
  44.                
  45.             while((fromServer = in.readLine()) != null){   
  46.                 if(fromServer.equals("bye"))   
  47.                     break;   
  48.                 else if(fromServer.equals("Hi")){   
  49.                     System.out.println("Do you want to get the 'PyNet.pdf' file?(y/n):");   
  50.                     fromUser = userIn.readLine();   
  51.                     if(fromUser.equals("y")){   
  52.                         System.out.println("Please input your username:");   
  53.                         fromUser = userIn.readLine();   
  54.                         out.println("username:" + fromUser);   
  55.                         out.flush();//notice: if this sentence is lost, the info will not arrive at server side   
  56.                     }else  
  57.                         break;   
  58.                 }else if(fromServer.startsWith("username")){   
  59.                     if(fromServer.split(":")[1].equals("valid")){   
  60.                         System.out.println("Please input your password:");   
  61.                         fromUser = userIn.readLine();   
  62.                         out.println("password:" + fromUser);   
  63.                         out.flush();   
  64.                     }else{   
  65.                         System.out.println("Please input your username:");   
  66.                         fromUser = userIn.readLine();   
  67.                         out.println("username:" + fromUser);   
  68.                         out.flush();   
  69.                     }   
  70.                 }else if(fromServer.startsWith("password")){   
  71.                     if(fromServer.split(":")[1].equals("valid")){   
  72.                         System.out.println("Downloading...");   
  73.                            
  74.                         //prepare for the receiving   
  75.                         File newFile = new File("new.pdf");   
  76.                         newFile.createNewFile();   
  77.                         RandomAccessFile raf = new RandomAccessFile(newFile, "rw");   
  78.                            
  79.                         InputStream fileInput = new DataInputStream(   
  80.                                 new BufferedInputStream(theSocket.getInputStream()));   
  81.                         byte[] buf = new byte[2048];   
  82.                            
  83.                         //receiving   
  84.                         int num = fileInput.read(buf);   
  85.                         while(num != -1){   
  86.                             raf.write(buf, 0, num);   
  87.                             raf.skipBytes(num);   
  88.                             num = fileInput.read(buf);   
  89.                         }   
  90.                            
  91.                         in.close();   
  92.                         raf.close();   
  93.                         System.out.println("File download is finished");   
  94.                         break;   
  95.                     }else{   
  96.                         System.out.println("Please input your password:");   
  97.                         fromUser = userIn.readLine();   
  98.                         out.println("password:" + fromUser);   
  99.                         out.flush();   
  100.                     }   
  101.                 }   
  102.             }   
  103.                
  104.             out.println("bye");   
  105.             out.flush();   
  106.                
  107.             out.close();   
  108.             in.close();   
  109.             userIn.close();   
  110.             theSocket.close();   
  111.         } catch (UnknownHostException e) {   
  112.             // TODO Auto-generated catch block   
  113.             e.printStackTrace();   
  114.         } catch (IOException e) {   
  115.             // TODO Auto-generated catch block   
  116.             e.printStackTrace();   
  117.         }   
  118.     }   
  119. }   

这个比较简单,除了用户交互外,最核心的就是line-71到line-94这段处理文件下载的了,当SimpleClient收到服务器的“password:valid"就表示用户身份验证通过,可以执行文件下载了。这部分也是混合使用了New IO和Old IO,原因和上边解释的一样。

这里还要强调一点的是在网络编程的时候,println以后,一定要flush以下。为什么呢?学过网络的人都知道,当一个package太小是,网络并不会把它发出去,而是等到足够大时。我在实际测试时,就忘记了在println后写flush,结果这边明明已经out出了,但Server端就是在那塞着,这个问题困扰了我半天,呵呵。

分享到:
评论

相关推荐

    一个小小的JAVA 关于data server/client 的小小程序

    可以用来做java聊天系统的底层构架。。很简单的小程序 演示数据怎样在服务器和客户端间传输...

    java实现简单实现C/S应用

    其中,Client端为GUI程序,用于提供界面输入两个数,并有1个“发送”按钮,另外还有一个 TextField用于显示传来的计算结果;Server端用于监听连接请求、计算发送过来的两数的和、送回数据结果。

    java语言通过Socket实现文件传输

    在Unix/Linux/Windows环境下,通过Socket方式实现一个基于Client/Server或P2P模式的文件传输程序。

    局域网传送文件java实现(Client)端

    本程序是局域网传送文件java实现(Client)端,配套还有Server端。

    SOCKET TCP 文件传输 客户端 服务器端 client svever

    SOCKET TCP 文件传输 客户端 服务器端 client svever 实现简易文件传输功能

    java cas server 集成 java cas client 和 net client

    java cas server 集成 java cas client 和 net client 附带文档,问题解决方法,源码,jar包,包含技术有java cas,cfx,LDAP,net cas。核心在于集成了net client,,上传太小,不能传源码,有需要的加群。有兴趣的...

    Java Socket PC端传输文件简易服务器客户端

    Java Socket编程 PC端程序 用来传输文件 包括服务器、客户端

    Java版本的点对点文件传输器的源码

    用BufferStream实现的点对点文件传输器,网络TCP协议实现 使用时先构建Server,然后由Client连接Server之后可以实现互传文件 已经修改了上个版本的传送方bug,并增加了等待Client时的对话框,方便Server端随时中断对...

    计算机网络tcp实验java实现

    计算机网络tcp实现,发包传包丢包问题,以及正确率计算

    java源码包---java 源码 大量 实例

    5个目标文件,演示Address EJB的实现,创建一个EJB测试客户端,得到名字上下文,查询jndi名,通过强制转型得到Home接口,getInitialContext()函数返回一个经过初始化的上下文,用client的getHome()函数调用Home接口...

    java -opc ua 连接kepserver 源码

    maven架构 idea编辑器 包含读取、写入、订阅、批量订阅、断点续传等功能、 下载后可直接运行 内容很简单,就一个类,采用匿名登录的方式,代码和测试全在一个类里面 注释很详细

    Socket文件传输(FTP)包含Server,Client

    这是一个用纯Socket和Java标准写成的文件传输软件,早出来这个轮子给大家看一看,不要大家去用,而是去共同学习,更好的用别人造出来的轮子,总之我这个文件传输思路是对的,可能和FTP协议不太吻合(我只用了一个21...

    java聊天和文件传输实例

    用java的NIO技术编写的 ...2. 拖拽文件可以实现文件传输功能,也可以是文件夹 3. 启动时可以选择server端或client端启动 4. 本人原创,学习NIO和java的网络通信一定会不错 5. 下载下来可以直接导致eclipse,然后运行

    Java Socket传输文件

    利用Java的Socket实现了文件传输功能。 可以传输各种大小的文件(KB,MB,GB)。 分为两个文件:Server和Client,可以直接运行。

    JAVA上百实例源码以及开源项目

    5个目标文件,演示Address EJB的实现,创建一个EJB测试客户端,得到名字上下文,查询jndi名,通过强制转型得到Home接口,getInitialContext()函数返回一个经过初始化的上下文,用client的getHome()函数调用Home接口...

    java源码包4

    5个目标文件,演示Address EJB的实现,创建一个EJB测试客户端,得到名字上下文,查询jndi名,通过强制转型得到Home接口,getInitialContext()函数返回一个经过初始化的上下文,用client的getHome()函数调用Home接口...

    java源码包2

    5个目标文件,演示Address EJB的实现,创建一个EJB测试客户端,得到名字上下文,查询jndi名,通过强制转型得到Home接口,getInitialContext()函数返回一个经过初始化的上下文,用client的getHome()函数调用Home接口...

    java源码包3

    5个目标文件,演示Address EJB的实现,创建一个EJB测试客户端,得到名字上下文,查询jndi名,通过强制转型得到Home接口,getInitialContext()函数返回一个经过初始化的上下文,用client的getHome()函数调用Home接口...

    JAVA上百实例源码以及开源项目源代码

    5个目标文件,演示Address EJB的实现,创建一个EJB测试客户端,得到名字上下文,查询jndi名,通过强制转型得到Home接口,getInitialContext()函数返回一个经过初始化的上下文,用client的getHome()函数调用Home接口...

    基于java的文件传输系统设计与实现毕业设计(说明报告+源代码)

    当用户完成工作后,可使用FTP将文件传回到Web服务器。采用这种方法,用户无需使用Telnet 登录到远程主机进行工作,这样就使Web服务器的更新工作变得如此的轻松。 和Internet上的许多环境一样,完整的Java应用环境...

Global site tag (gtag.js) - Google Analytics