您好,登錄后才能下訂單哦!
本篇內容介紹了“如何利用NIO建立Socket服務器”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
傳統的Java 的IO,利用Socket建立服務器,接收客戶端連接,一般都是為每一個連接建立一個線程,如果連接數巨大,那么服務器開銷也將巨大。。NIO的原理,可以參照圖:
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代碼如下:
import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class ServerMain { public static void main(String[] args) { class SocketThread extends Thread{ private Socket socket; private byte[] buf; private int len = 0; public SocketThread(Socket socket) { this.socket = socket; buf = new byte[1024]; } @Override public void run() { try { DataInputStream dis = new DataInputStream(socket.getInputStream()); DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); //String command = dis.readUTF(); len = dis.read(buf); String command = new String(buf,0,len); System.out.println("command=="+command); String[] temp =command.split(" "); command = temp[0]; //命令 是put還是get String filename = temp[1]; //文件名 File file = new File("C:\\",filename);//假設放在C盤 if(command.equals("get")){ if(!file.exists()){ //dos.writeUTF("notexists"); dos.write("notexists".getBytes()); dos.flush(); System.out.println("沒有這個文件,無法提供下載!"); dis.close(); dos.close(); socket.close(); return; } //dos.writeUTF("DownloadReady "+file.length()); dos.write("準備下載".getBytes()); dos.flush(); System.out.println("正在接受文件下載..."); DataInputStream fis = new DataInputStream(new BufferedInputStream(new FileInputStream(file))); while ((len = fis.read(buf))!= -1) { dos.write(buf, 0, len); } dos.flush(); fis.close(); System.out.println("文件傳輸完成"); } else { //dos.writeUTF("UploadReady"); dos.write("UploadReady".getBytes()); dos.flush(); System.out.println("正在接受文件上傳..."); DataOutputStream fileOut = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file))); while ((len = dis.read(buf))!=-1) { fileOut.write(buf, 0, len); } System.out.println("上傳完畢!"); fileOut.close(); } dis.close(); dos.close(); socket.close(); } catch (Exception e) { e.printStackTrace(); } } } System.out.println("等待客戶端連接...."); int index = 0; try { ServerSocket server = new ServerSocket(9527,300); //端口號9527 允許***連接數300 while (true) { Socket socket = server.accept(); System.out.println("收到第"+(++index)+"個連接"); new SocketThread(socket).start(); //對每個連接創建一個線程 } } catch (Exception e) { e.printStackTrace(); } } }
使用NIO建立的Socket服務器,代碼如下:
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.util.Iterator; public class NewSocketServer { private static final int port = 9527; private Selector selector; private ByteBuffer clientBuffer = ByteBuffer.allocate(1024); private CharsetDecoder decoder = Charset.forName("GB2312").newDecoder(); private CharsetEncoder encoder = Charset.forName("GB2312").newEncoder(); //編碼解碼格式設置成GBK也行.UTF-8不行,中文亂碼 (前提都是客戶端沒有設置任何編碼解碼格式) public void setListener() throws Exception{ selector = Selector.open(); //打開選擇器 ServerSocketChannel server = ServerSocketChannel.open(); //定義一個 ServerSocketChannel通道 server.socket().bind(new InetSocketAddress(port)); //ServerSocketChannel綁定端口 server.configureBlocking(false); //配置通道使用非阻塞模式 server.register(selector, SelectionKey.OP_ACCEPT); //該通道在selector上注冊 接受連接的動作 while(true) { selector.select(); //select() 會阻塞,直到在該selector上注冊的channel有對應的消息讀入 Iterator iter = selector.selectedKeys().iterator(); while (iter.hasNext()) { SelectionKey key = (SelectionKey) iter.next(); iter.remove(); // 刪除此消息 process(key); // 當前線程內處理。(為了高效,一般會在另一個線程中處理此消息) } } } private void process(SelectionKey key) throws IOException { if (key.isAcceptable()) { // 接收請求 ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel channel = server.accept();//類似于io的socket,ServerSocketChannel的accept函數返回 SocketChannel channel.configureBlocking(false); //設置非阻塞模式 SelectionKey sKey = channel.register(selector, SelectionKey.OP_READ); sKey.attach("read_command"); //這兒接收到連接請求之后可以為每個連接設置一個ID } else if (key.isReadable()) { // 讀信息 SocketChannel channel = (SocketChannel) key.channel(); String name = (String) key.attachment(); if(name.equals("read_command")){ int count = channel.read(clientBuffer); if (count > 0) { clientBuffer.flip(); CharBuffer charBuffer = decoder.decode(clientBuffer); String command = charBuffer.toString(); //command形如:get abc.png 或者 put aaa.png System.out.println("command===="+command); //得到客戶端傳來的命令 String[] temp =command.split(" "); command = temp[0]; //命令 是put還是get String filename = temp[1]; //文件名 SelectionKey sKey = channel.register(selector,SelectionKey.OP_WRITE); if(command.equals("put"))sKey.attach("UploadReady#"+filename); //要保護該通道的文件名 else if(command.equals("get")){ if(!new File("C:\\",filename).exists()){ //假設文件都是在C盤根目錄 System.out.println("沒有這個文件,無法提供下載!"); sKey.attach("notexists"); } else sKey.attach("DownloadReady#"+filename); //要保護該通道的文件名 } } else { channel.close(); } } else if(name.startsWith("read_file")){//這兒可以新開一個線程 文件操作也可以用NIO DataOutputStream fileOut = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( new File("C:\\",name.split("#")[1])))); int passlen = channel.read(clientBuffer); while (passlen>=0) { clientBuffer.flip(); fileOut.write(clientBuffer.array(), 0, passlen); passlen = channel.read(clientBuffer); } System.out.println("上傳完畢!"); fileOut.close(); channel.close(); } clientBuffer.clear(); } else if (key.isWritable()) { // 寫事件 SocketChannel channel = (SocketChannel) key.channel(); String flag = (String) key.attachment(); if(flag.startsWith("downloading")){//這兒可以新開一個線程 文件操作也可以用NIO DataInputStream fis = new DataInputStream( new BufferedInputStream( new FileInputStream( new File("C:\\",flag.split("#")[1])))); byte[] buf = new byte[1024]; int len =0; while ((len = fis.read(buf))!= -1) { channel.write(ByteBuffer.wrap(buf, 0, len)); } fis.close(); System.out.println("文件傳輸完成"); channel.close(); } else if(flag.equals("notexists")){ //channel.write(encoder.encode(CharBuffer.wrap(flag))); channel.write(ByteBuffer.wrap(flag.getBytes())); //不用編碼也行 客戶端直接接收 中文也不是亂碼 channel.close(); } else if(flag.startsWith("UploadReady")){ channel.write(encoder.encode(CharBuffer.wrap("UploadReady"))); //這兒如果不重新注冊該通道的讀操作 selector選擇到該通道的將繼續永遠是寫操作,也就無法跳轉到上面的接受上傳的處理 SelectionKey sKey =channel.register(selector, SelectionKey.OP_READ);//register是覆蓋的????!!! sKey.attach("read_file#"+flag.split("#")[1]); //key.attach("read_file#"+flag.split("#")[1]); //select不到讀操作 } else if(flag.startsWith("DownloadReady")){ channel.write(ByteBuffer.wrap("準備下載".getBytes())); //channel.write(encoder.encode(CharBuffer.wrap("準備下載"))); key.attach("downloading#"+flag.split("#")[1]); } } } public static void main(String[] args) { try { System.out.println("等待來至" + port + "端口的客戶端連接....."); new NewSocketServer().setListener(); } catch (Exception e) { e.printStackTrace(); } } }
客戶端代碼如下:
import java.io.*; import java.net.InetAddress; import java.net.Socket; import java.util.Scanner; public class ClientMain { private int ServerPort = 9527; private String ServerAddress = "192.168.1.154"; private String GetOrPut = "get"; private String local_filename = ""; private String remote_filename = ""; private byte[] buf; private int len; class SocketThread extends Thread{ @Override public void run() { try { File file = new File("C:\\",local_filename); //假設文件放在C盤 if(!file.exists()&&GetOrPut.equals("put")){ System.out.println("本地沒有這個文件,無法上傳!"); return; } InetAddress loalhost = InetAddress.getLocalHost(); Socket socket = new Socket(ServerAddress,ServerPort,loalhost,44); //服務器IP地址 端口號 本機IP 本機端口號 DataInputStream dis = new DataInputStream(socket.getInputStream()); DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); //dos.writeUTF(GetOrPut+" "+remote_filename);//服務器端如果是io的socket,writeUTF和writeUTF對接 dos.write((GetOrPut+" "+remote_filename).getBytes()); dos.flush(); //String tempString = dis.writeUTF(); buf = new byte[1024]; len = dis.read(buf); String tempString = new String(buf,0,len);//服務器反饋的信息 //System.out.println(tempString); if(tempString.equals("notexists")){ System.out.println("服務器沒有這個文件,無法下載!"); dos.close(); dis.close(); socket.close(); return; } if(tempString.startsWith("準備下載")){ DataOutputStream fileOut = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file))); while ((len = dis.read(buf))!=-1) { fileOut.write(buf, 0, len); } System.out.println("下載完畢!"); fileOut.close(); dos.close(); dis.close(); socket.close(); } else if(tempString.equals("UploadReady")){ System.out.println("正在上傳文件......."); DataInputStream fis = new DataInputStream(new BufferedInputStream(new FileInputStream(file))); while ((len = fis.read(buf))!= -1) { dos.write(buf, 0, len); } dos.flush(); System.out.println("上傳完畢!"); fis.close(); dis.close(); dos.close(); socket.close(); } } catch (Exception e) { e.printStackTrace(); } } } public boolean checkCommand(String command) { if(!command.startsWith("put")&&!command.startsWith("get")){ System.out.println("輸入命令錯誤"); return false; } int index = -1; String temp = ""; String[] tempStrings = null; if((index=command.indexOf("-h"))>0){ temp = command.substring(index+3); temp = temp.substring(0, temp.indexOf(' ')); ServerAddress = temp; } if((index=command.indexOf("-p"))>0){ temp = command.substring(index+3); temp = temp.substring(0, temp.indexOf(' ')); ServerPort = Integer.valueOf(temp); } tempStrings = command.split(" "); if(command.startsWith("put")){ GetOrPut = "put"; local_filename = tempStrings[tempStrings.length-2]; remote_filename = tempStrings[tempStrings.length-1]; } else if(command.startsWith("get")){ GetOrPut = "get"; local_filename = tempStrings[tempStrings.length-1]; remote_filename = tempStrings[tempStrings.length-2]; } return true; } public static void main(String[] args) { ClientMain thisC= new ClientMain(); Scanner sc = new Scanner(System.in); String commandString = ""; do { System.out.println("請輸入命令:"); commandString = sc.nextLine(); } while (!thisC.checkCommand(commandString)); ClientMain.SocketThread a = thisC.new SocketThread(); a.start(); } }
“如何利用NIO建立Socket服務器”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。