• 利用NIO建立Socket服务器


    传统的Java 的IO,利用Socket建立服务器,接收客户端连接,一般都是为每一个连接建立一个线程,如果连接数巨大,那么服务器开销也将巨大。。NIO的原理,可以参照图:http://new.51cto.com/files/uploadimg/20080912/150103487.jpg



    Socket的Channel在Selector上注册某一种动作,Selector通过select操作,监视所有在该Selector注册过的Channel的对应的动作,如果监测到某一对应的动作,则返回selectedKeys,自己手动取到各个SelectionKey进行相应的处理。当然NIO不仅可以接受Socket的Channel,还有文件操作等其他IO操作。

    作业的要求:

    使用socket编程实现一个简单的文件服务器。客户端程序实现put功能(将一个文件从本地传到文件服务器)和get功能(从文件服务器取一远程文件存为本地文件)。客户端和文件服务器不在同一台机器上。
    put [-h hostname] [-p portname] local_filename remote_filename
    get [-h hostname] [-p portname] remote_filename local_filename

    服务器端不使用nio,直接使用io的socket代码如下:

    Java代码  收藏代码
    1. import java.io.*;  
    2. import java.net.ServerSocket;  
    3. import java.net.Socket;  
    4.   
    5. public class ServerMain {  
    6.       
    7.     public static void main(String[] args) {  
    8.   
    9.         class SocketThread extends Thread{  
    10.               
    11.             private Socket socket;  
    12.             private byte[] buf;  
    13.             private int len = 0;  
    14.             public SocketThread(Socket socket) {   
    15.                 this.socket = socket;  
    16.                 buf = new byte[1024];  
    17.             }  
    18.   
    19.             @Override  
    20.             public void run() {  
    21.                 try {     
    22.                     DataInputStream dis = new DataInputStream(socket.getInputStream());  
    23.                     DataOutputStream dos = new DataOutputStream(socket.getOutputStream());   
    24.                       
    25.                     //String command = dis.readUTF();   
    26.                     len = dis.read(buf);  
    27.                     String command = new String(buf,0,len);  
    28.                       
    29.                     System.out.println("command=="+command);  
    30.                       
    31.                     String[] temp =command.split(" ");  
    32.                     command = temp[0];  //命令  是put还是get  
    33.                     String filename = temp[1];  //文件名  
    34.                       
    35.                     File file = new File("C:\",filename);//假设放在C盘  
    36.                     if(command.equals("get")){  
    37.                         if(!file.exists()){  
    38.                             //dos.writeUTF("notexists");  
    39.                             dos.write("notexists".getBytes());  
    40.                             dos.flush();  
    41.                             System.out.println("没有这个文件,无法提供下载!");  
    42.                             dis.close();  
    43.                             dos.close();  
    44.                             socket.close();  
    45.                             return;  
    46.                         }  
    47.                         //dos.writeUTF("DownloadReady "+file.length());   
    48.                         dos.write("准备下载".getBytes());  
    49.                         dos.flush();  
    50.                           
    51.                         System.out.println("正在接受文件下载...");  
    52.                         DataInputStream fis = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));   
    53.     
    54.                         while ((len = fis.read(buf))!= -1) {   
    55.                             dos.write(buf, 0, len);  
    56.                         }  
    57.                         dos.flush();  
    58.                           
    59.                         fis.close();       
    60.                         System.out.println("文件传输完成");  
    61.                     }  
    62.                     else {   
    63.                         //dos.writeUTF("UploadReady");   
    64.                         dos.write("UploadReady".getBytes());  
    65.                         dos.flush();  
    66.                           
    67.                         System.out.println("正在接受文件上传...");  
    68.                         DataOutputStream fileOut =   
    69.                             new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));  
    70.    
    71.                         while ((len = dis.read(buf))!=-1) {     
    72.                             fileOut.write(buf, 0, len);  
    73.                         }  
    74.                         System.out.println("上传完毕!");  
    75.                         fileOut.close();   
    76.                     }  
    77.                     dis.close();  
    78.                     dos.close();  
    79.                     socket.close();   
    80.                 } catch (Exception e) {  
    81.                     e.printStackTrace();  
    82.                 }  
    83.             }  
    84.               
    85.         }  
    86.           
    87.         System.out.println("等待客户端连接....");  
    88.         int index = 0;  
    89.         try {  
    90.             ServerSocket server = new ServerSocket(9527,300); //端口号9527  允许最大连接数300  
    91.             while (true) {  
    92.                 Socket socket = server.accept();  
    93.                 System.out.println("收到第"+(++index)+"个连接");  
    94.                 new SocketThread(socket).start(); //对每个连接创建一个线程  
    95.             }  
    96.         } catch (Exception e) {  
    97.             e.printStackTrace();  
    98.         }  
    99.     }  
    100.   
    101. }  

    使用NIO建立的Socket服务器,代码如下:

    Java代码  收藏代码
    1. import java.io.BufferedInputStream;  
    2. import java.io.BufferedOutputStream;  
    3. import java.io.DataInputStream;  
    4. import java.io.DataOutputStream;  
    5. import java.io.File;  
    6. import java.io.FileInputStream;  
    7. import java.io.FileOutputStream;  
    8. import java.io.IOException;  
    9. import java.net.InetSocketAddress;  
    10. import java.nio.ByteBuffer;   
    11. import java.nio.CharBuffer;  
    12. import java.nio.channels.SelectionKey;  
    13. import java.nio.channels.Selector;  
    14. import java.nio.channels.ServerSocketChannel;  
    15. import java.nio.channels.SocketChannel;  
    16. import java.nio.charset.Charset;  
    17. import java.nio.charset.CharsetDecoder;  
    18. import java.nio.charset.CharsetEncoder;  
    19. import java.util.Iterator;  
    20.   
    21. public class NewSocketServer {  
    22.    
    23.     private static final int port = 9527;  
    24.     private Selector selector;  
    25.     private ByteBuffer clientBuffer = ByteBuffer.allocate(1024);    
    26.     private CharsetDecoder decoder = Charset.forName("GB2312").newDecoder();  
    27.     private CharsetEncoder encoder = Charset.forName("GB2312").newEncoder();   
    28.     //编码解码格式设置成GBK也行.UTF-8不行,中文乱码  (前提都是客户端没有设置任何编码解码格式)  
    29.       
    30.     public void setListener() throws Exception{  
    31.           
    32.         selector = Selector.open(); //打开选择器     
    33.           
    34.         ServerSocketChannel server = ServerSocketChannel.open();  //定义一个 ServerSocketChannel通道  
    35.         server.socket().bind(new InetSocketAddress(port));  //ServerSocketChannel绑定端口    
    36.         server.configureBlocking(false);   //配置通道使用非阻塞模式  
    37.         server.register(selector, SelectionKey.OP_ACCEPT); //该通道在selector上注册  接受连接的动作  
    38.           
    39.         while(true)  
    40.         {      
    41.             selector.select();   //select() 会阻塞,直到在该selector上注册的channel有对应的消息读入  
    42.             Iterator iter = selector.selectedKeys().iterator();     
    43.             while (iter.hasNext()) {      
    44.                 SelectionKey key = (SelectionKey) iter.next();     
    45.                 iter.remove();  // 删除此消息   
    46.                 process(key);   // 当前线程内处理。(为了高效,一般会在另一个线程中处理此消息)  
    47.             }     
    48.         }     
    49.     }  
    50.       
    51.      private void process(SelectionKey key) throws IOException {     
    52.             if (key.isAcceptable()) { // 接收请求     
    53.                 ServerSocketChannel server = (ServerSocketChannel) key.channel();     
    54.                 SocketChannel channel = server.accept();//类似于io的socket,ServerSocketChannel的accept函数返回 SocketChannel  
    55.                 channel.configureBlocking(false);   //设置非阻塞模式     
    56.                 SelectionKey sKey = channel.register(selector, SelectionKey.OP_READ);   
    57.                 sKey.attach("read_command"); //这儿接收到连接请求之后可以为每个连接设置一个ID  
    58.             }   
    59.             else if (key.isReadable()) { // 读信息      
    60.                 SocketChannel channel = (SocketChannel) key.channel();     
    61.                 String name = (String) key.attachment();   
    62.                 if(name.equals("read_command")){  
    63.                     int count = channel.read(clientBuffer);   
    64.                     if (count > 0) {     
    65.                         clientBuffer.flip();     
    66.                         CharBuffer charBuffer = decoder.decode(clientBuffer);     
    67.                         String command = charBuffer.toString();     
    68.                           
    69.                         //command形如:get abc.png 或者  put aaa.png  
    70.                         System.out.println("command===="+command);  //得到客户端传来的命令   
    71.                           
    72.                         String[] temp =command.split(" ");  
    73.                         command = temp[0];  //命令  是put还是get  
    74.                         String filename = temp[1];  //文件名  
    75.                           
    76.                         SelectionKey sKey = channel.register(selector,SelectionKey.OP_WRITE);     
    77.                         if(command.equals("put"))sKey.attach("UploadReady#"+filename);  //要保护该通道的文件名  
    78.                         else if(command.equals("get")){   
    79.                             if(!new File("C:\",filename).exists()){ //假设文件都是在C盘根目录  
    80.                                 System.out.println("没有这个文件,无法提供下载!");  
    81.                                 sKey.attach("notexists");   
    82.                             }  
    83.                             else sKey.attach("DownloadReady#"+filename); //要保护该通道的文件名  
    84.                         }  
    85.                     } else {     
    86.                         channel.close();     
    87.                     }     
    88.                 }  
    89.                 else if(name.startsWith("read_file")){//这儿可以新开一个线程     文件操作也可以用NIO   
    90.                     DataOutputStream fileOut =   
    91.                         new DataOutputStream(  
    92.                                 new BufferedOutputStream(  
    93.                                         new FileOutputStream(  
    94.                                                 new File("C:\",name.split("#")[1]))));  
    95.    
    96.                     int passlen = channel.read(clientBuffer);    
    97.                     while (passlen>=0) {     
    98.                         clientBuffer.flip();    
    99.                         fileOut.write(clientBuffer.array(), 0, passlen);   
    100.                         passlen = channel.read(clientBuffer);  
    101.                     }  
    102.                     System.out.println("上传完毕!");  
    103.                     fileOut.close();   
    104.                     channel.close();  
    105.                 }  
    106.                 clientBuffer.clear();     
    107.             }   
    108.             else if (key.isWritable()) { // 写事件     
    109.                 SocketChannel channel = (SocketChannel) key.channel();     
    110.                 String flag = (String) key.attachment();      
    111.                 if(flag.startsWith("downloading")){//这儿可以新开一个线程   文件操作也可以用NIO  
    112.                     DataInputStream fis = new DataInputStream(  
    113.                             new BufferedInputStream(  
    114.                                     new FileInputStream(  
    115.                                             new File("C:\",flag.split("#")[1]))));   
    116.                        
    117.                     byte[] buf = new byte[1024];  
    118.                     int len =0;   
    119.                     while ((len = fis.read(buf))!= -1) {   
    120.                         channel.write(ByteBuffer.wrap(buf, 0, len));    
    121.                     }    
    122.                     fis.close();       
    123.                     System.out.println("文件传输完成");  
    124.                     channel.close();  
    125.                 }  
    126.                 else if(flag.equals("notexists")){   
    127.                     //channel.write(encoder.encode(CharBuffer.wrap(flag)));     
    128.                     channel.write(ByteBuffer.wrap(flag.getBytes())); //不用编码也行    客户端直接接收    中文也不是乱码  
    129.                     channel.close();  
    130.                 }  
    131.                 else if(flag.startsWith("UploadReady")){   
    132.                     channel.write(encoder.encode(CharBuffer.wrap("UploadReady")));   
    133.                       
    134.                     //这儿如果不重新注册该通道的读操作    selector选择到该通道的将继续永远是写操作,也就无法跳转到上面的接受上传的处理  
    135.                     SelectionKey sKey =channel.register(selector, SelectionKey.OP_READ);//register是覆盖的????!!!  
    136.                     sKey.attach("read_file#"+flag.split("#")[1]);  
    137.                     //key.attach("read_file#"+flag.split("#")[1]); //select不到读操作  
    138.                 }  
    139.                 else if(flag.startsWith("DownloadReady")){   
    140.                     channel.write(ByteBuffer.wrap("准备下载".getBytes()));   
    141.                     //channel.write(encoder.encode(CharBuffer.wrap("准备下载")));     
    142.                     key.attach("downloading#"+flag.split("#")[1]);  
    143.                 }   
    144.             }    
    145.         }     
    146.       
    147.     public static void main(String[] args) {  
    148.           
    149.         try {  
    150.              System.out.println("等待来至" + port + "端口的客户端连接.....");   
    151.             new NewSocketServer().setListener();  
    152.         } catch (Exception e) {  
    153.             e.printStackTrace();  
    154.         }  
    155.   
    156.     }  
    157.   
    158. }  

     客户端代码如下:

    Java代码  收藏代码
    1. import java.io.*;  
    2. import java.net.InetAddress;  
    3. import java.net.Socket;  
    4. import java.util.Scanner;  
    5.   
    6. public class ClientMain {  
    7.   
    8.     private   int ServerPort = 9527;  
    9.     private   String ServerAddress = "192.168.1.154";  
    10.     private   String GetOrPut = "get";     
    11.     private   String local_filename = "";   
    12.     private   String remote_filename  = "";   
    13.     private   byte[] buf;  
    14.     private   int len;  
    15.     class SocketThread extends Thread{  
    16.           
    17.         @Override  
    18.         public void run() {  
    19.              try {  
    20.                   
    21.                 File file = new File("C:\",local_filename); //假设文件放在C盘  
    22.                 if(!file.exists()&&GetOrPut.equals("put")){   
    23.                     System.out.println("本地没有这个文件,无法上传!");   
    24.                     return;  
    25.                 }   
    26.                   
    27.                 InetAddress loalhost = InetAddress.getLocalHost();  
    28.                 Socket socket = new Socket(ServerAddress,ServerPort,loalhost,44);  
    29.                                             //服务器IP地址  端口号   本机IP 本机端口号  
    30.                 DataInputStream dis = new DataInputStream(socket.getInputStream());  
    31.                 DataOutputStream dos = new DataOutputStream(socket.getOutputStream());  
    32.                    
    33.                 //dos.writeUTF(GetOrPut+" "+remote_filename);//服务器端如果是io的socket,writeUTF和writeUTF对接  
    34.                 dos.write((GetOrPut+" "+remote_filename).getBytes());  
    35.                 dos.flush();   
    36.                 
    37.                 //String tempString = dis.writeUTF();   
    38.                 buf = new byte[1024];  
    39.                 len = dis.read(buf);  
    40.                 String tempString = new String(buf,0,len);//服务器反馈的信息  
    41.                   
    42.                 //System.out.println(tempString);   
    43.                 if(tempString.equals("notexists")){  
    44.                     System.out.println("服务器没有这个文件,无法下载!");   
    45.                     dos.close();  
    46.                     dis.close();  
    47.                     socket.close();  
    48.                     return;  
    49.                 }  
    50.                   
    51.                 if(tempString.startsWith("准备下载")){    
    52.                     DataOutputStream fileOut =   
    53.                         new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));  
    54.    
    55.                     while ((len = dis.read(buf))!=-1) {     
    56.                         fileOut.write(buf, 0, len);  
    57.                     }  
    58.                     System.out.println("下载完毕!");  
    59.                     fileOut.close();  
    60.                     dos.close();  
    61.                     dis.close();  
    62.                     socket.close();  
    63.                 }  
    64.                 else if(tempString.equals("UploadReady")){    
    65.                     System.out.println("正在上传文件.......");  
    66.                     DataInputStream fis = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));   
    67.                         
    68.                     while ((len = fis.read(buf))!= -1) {    
    69.                         dos.write(buf, 0, len);  
    70.                     }  
    71.                     dos.flush();  
    72.                     System.out.println("上传完毕!");  
    73.                     fis.close();  
    74.                     dis.close();  
    75.                     dos.close();  
    76.                     socket.close();  
    77.                 }  
    78.                   
    79.             } catch (Exception e) {  
    80.                 e.printStackTrace();  
    81.             }  
    82.         }  
    83.           
    84.     }  
    85.       
    86.     public boolean checkCommand(String command)  
    87.     {   
    88.         if(!command.startsWith("put")&&!command.startsWith("get")){  
    89.             System.out.println("输入命令错误");  
    90.             return false;  
    91.         }  
    92.           
    93.         int index = -1;  
    94.         String temp = "";  
    95.         String[] tempStrings = null;  
    96.           
    97.         if((index=command.indexOf("-h"))>0){  
    98.             temp = command.substring(index+3);  
    99.             temp = temp.substring(0, temp.indexOf(' '));  
    100.             ServerAddress = temp;  
    101.         }  
    102.         if((index=command.indexOf("-p"))>0){  
    103.             temp = command.substring(index+3);  
    104.             temp = temp.substring(0, temp.indexOf(' '));  
    105.             ServerPort = Integer.valueOf(temp);  
    106.         }  
    107.           
    108.         tempStrings = command.split(" ");  
    109.         if(command.startsWith("put")){  
    110.             GetOrPut = "put";  
    111.             local_filename = tempStrings[tempStrings.length-2];  
    112.             remote_filename = tempStrings[tempStrings.length-1];  
    113.         }  
    114.         else if(command.startsWith("get")){  
    115.             GetOrPut = "get";  
    116.             local_filename = tempStrings[tempStrings.length-1];  
    117.             remote_filename = tempStrings[tempStrings.length-2];  
    118.         }  
    119.           
    120.         return true;  
    121.     }  
    122.       
    123.     public static void main(String[] args) {  
    124.         ClientMain thisC= new ClientMain();   
    125.         Scanner sc = new Scanner(System.in);  
    126.         String commandString = "";  
    127.         do {  
    128.             System.out.println("请输入命令:");   
    129.             commandString = sc.nextLine();  
    130.         } while (!thisC.checkCommand(commandString));   
    131.           
    132.         ClientMain.SocketThread a = thisC.new SocketThread();  
    133.         a.start();  
    134.        
    135.     }  
    136.   
    137. }  

    分享到:  
    评论
    2 楼 lishuoying 2013-10-08  
    读取命令有小小问题

    Java代码  收藏代码
    1. int count = channel.read(clientBuffer);     
    2.                     if (count > 0) {   
    3. //命令解释  
    4.  }  


    channel.read(clientBuffer) 有可能接收到命令字节的一部分,命令解释会出问题
    1 楼 chinesejie 2012-11-07  
    同学,您的程序有bug,我帮你调了几天了,终于搞定。
    用nio写的server这段。
    while (passlen>=0) {     
                            clientBuffer.flip();    
                            fileOut.write(clientBuffer.array(), 0, passlen);   
                            passlen = channel.read(clientBuffer);  
                        }

    应嘎是这样的逻辑:
    while (passlen>=0) {   
    if(passlen!=0) {  
                            clientBuffer.flip(); 
    }   
                            fileOut.write(clientBuffer.array(), 0, passlen);   
                            passlen = channel.read(clientBuffer);  
                        }    
    这样传大文件也不都会错了。
  • 相关阅读:
    5分钟带你了解Kafka的技术架构
    聊聊我的知识体系
    你分得清楚Maven的聚合和继承吗?
    为抖音而生的多闪,如何获取抖音的用户数据?
    消息中间件系列第3讲:使用消息队列需要考虑的几个问题
    消息中间件系列第2讲:如何进行消息队列选型?
    消息中间件系列第1讲:为什么要用消息队列?
    JVM规范系列开篇:为什么要读JVM规范?
    安全编码实践之三:身份验证和会话管理防御
    安全编码实践之二:跨站脚本攻击防御
  • 原文地址:https://www.cnblogs.com/fx2008/p/4024904.html
Copyright © 2020-2023  润新知