摘要:在設(shè)定時間內(nèi)接收到相應(yīng)操作的請求則返回可以處理請求的數(shù)量,否則在超時后返回,程序繼續(xù)執(zhí)行。使用接收請求并處理接收到請求后調(diào)用返回的集合。保存了處理當(dāng)前請求的和,并提供了不同的操作類型。默認值為且其值必須小于的值。
Java中的Socket可以分為普通Socket和NioSocket兩種。 普通Socket的用法
Java中的網(wǎng)絡(luò)通信是通過Socket實現(xiàn)的,Socket分為ServerSocket和Socket兩大類,ServerSocket用于服務(wù)端,可以通過accept方法監(jiān)聽請求,監(jiān)聽到請求后返回Socket,Socket用于具體完成數(shù)據(jù)傳輸,客戶端直接使用Socket發(fā)起請求并傳輸數(shù)據(jù)。
一個簡單的交互介紹ServerSocket及Socket的使用: 1. 創(chuàng)建ServerSocket。ServerSocket的構(gòu)造方法一共有5個。最方便的是傳入一個端口參數(shù)的方法。2. 調(diào)用創(chuàng)建出來的ServerSocket的accept方法進行監(jiān)聽
accept方法是阻塞方法,也就是說調(diào)用accept方法后程序會停下來等待連接請求,在接收到請求之前程序?qū)⒉粫^續(xù)執(zhí)行,當(dāng)接收到請求之后,accept方法會返回一個Socket。3. 使用accept方法返回的Socket與客戶端進行通信。
服務(wù)端代碼示例:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.text.SimpleDateFormat; import java.util.Date; public class Server { public static void main(String[] args) { try { //創(chuàng)建一個ServeSocket,設(shè)置端口為8080 ServerSocket serverSocket = new ServerSocket(8080); //運行Socket監(jiān)聽,等待請求 此方法會阻塞線程,當(dāng)有請求時才會繼續(xù)執(zhí)行 Socket socket = serverSocket.accept(); //接收到請求之后使用Socket進行通信,創(chuàng)建BufferedReader用于讀取請求的數(shù)據(jù) BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter out = new PrintWriter(socket.getOutputStream()); String line = in.readLine(); System.out.println(line); //創(chuàng)建PrintlnWriter,用于發(fā)送數(shù)據(jù) out.println("已經(jīng)接受到了數(shù)據(jù)"); out.flush(); System.out.println("Server關(guān)閉" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss").format(new Date())); //關(guān)閉資源 out.close(); in.close(); socket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } finally { } } }
客戶端代碼示例:
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.text.SimpleDateFormat; import java.util.Date; /** * 客戶端 * @author sanchan */ public class Client { public static void main(String[] args) { //需要先啟動Server否則報錯java.net.ConnectException: Connection refused try { String msg="你好,ServerSocket!"; //創(chuàng)建一個Socket,與本機8080端口連接 Socket socket=new Socket("127.0.0.1",8080); //使用Socket創(chuàng)建PrintWriter和BufferedReader進行數(shù)據(jù)的讀寫 PrintWriter out=new PrintWriter(socket.getOutputStream()); out.println(msg); out.flush(); BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream())); String line=in.readLine(); System.out.println(line); //關(guān)閉資源 in.close(); out.close(); socket.close(); System.out.println("client關(guān)閉"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss").format(new Date())); } catch (Exception e) { e.printStackTrace(); } finally { } } }NioSocket的使用
nio(new IO)是JDK1.4新增加的IO模式,nio在底層采用了與新的處理方式大大的提高了Java IO的效率。Socket也屬于IO的一種,nio提供了相對應(yīng)的類:ServerSocketChannel和SocketChannel,分別對應(yīng)原來的ServerSocket和Socket。
理解nio三基礎(chǔ): 1. Buffer 2. Channel 3. Selector 小故事里有大智慧:試想一下如果電商是只要有訂單就派人直接取貨去送貨【這種模式就相當(dāng)于之前的Socket方式】,而不是現(xiàn)在的快遞模式:將貨物統(tǒng)一到中轉(zhuǎn)站,分揀員按照配送范圍分配快遞員,然后快遞員統(tǒng)一送貨【這種模式就相當(dāng)于NioSocket模式】。那么你得多長時間收到你得快遞/(ㄒoㄒ)/~~
Buffer就是貨物,Channel就是快遞員,Selector就是中轉(zhuǎn)站的分揀員。
SerSocketChannel可以使用自身的靜態(tài)工廠方法open創(chuàng)建。 每個ServerSocketChannel對應(yīng)一個ServerSocket,可以調(diào)用其socket方法來獲取【不過如果使用該ServerSocket監(jiān)聽請求就又回到原來的 普通Socket 模式了,一般只用于使用bind方法綁定端口】。 ServerSocketChannel可以通過`SelectableChannel configureBlocking(boolean block)` 方法來設(shè)置是否采用阻塞模式。設(shè)置非阻塞模式之后可以調(diào)用register方法注冊Selector【阻塞模式不可以使用Selector】2. 創(chuàng)建Selector并注冊Selector到ServerSocketChannel上
Selector可以使用自身的靜態(tài)工廠方法open創(chuàng)建。 創(chuàng)建后通過上面所說的Channel的register方法注冊到ServerSocketChannel或者SocketChannel上。3.調(diào)用Selector的select方法等待請求
通過select方法等待請求,select方法可傳入代表最長等待時間的long型參數(shù)。在設(shè)定時間內(nèi)接收到相應(yīng)操作的請求則返回可以處理請求的數(shù)量,否則在超時后返回0,程序繼續(xù)執(zhí)行。如果傳入0或者使用無參的重載方法,則會采用阻塞模式直到有相應(yīng)操作的請求出現(xiàn)。4. 使用Selector接收請求并處理
接收到請求后Selector調(diào)用selectedKeys返回SelectionKey的Set集合。5. 使用SelectionKey獲取到Channel、Selector和操作類型并進行具體操作。
SelectionKey保存了處理當(dāng)前請求的Channel和Selector,并提供了不同的操作類型。前面提到的Channel注冊Selector的register方法參數(shù)中第二個參數(shù)就是SelectionKey定義的。共有四種:
SelectionKey.OP_ACCEPT //請求操作 SelectionKey.OP_CONNECT //鏈接操作 SelectionKey.OP_READ //讀操作 SelectionKey.OP_WRITE //寫操作
只有在register方法中注冊了對應(yīng)的操作Selector才會關(guān)心相應(yīng)類型操作的請求。
Selector和Channel是多對多關(guān)系。 Selector是按不同的操作類型進行分揀,將分揀結(jié)果保存在SelectionKey中,可分別通過SelectionKey的channel、selector方法來獲取對應(yīng)的Channel和Selector。可以使用SelectionKey的isAcceptable、isConnectable、isReadable和isWritable方法來判斷是什么類型的操作。Buffer是專門用于存儲數(shù)據(jù),有四個極為重要的屬性:
capacity:容量。
Buffer最多可以保存元素的數(shù)量,創(chuàng)建時設(shè)置,使用過程中不可修改。
limit:可以使用的上限。
剛創(chuàng)建Buffer時limit等于capacity。如果給limit設(shè)置【不能超過capacity】之后,limit就成了最大可訪問的值。
例如,一個Buffer的capacity為100,表示最多可以保存100個數(shù)據(jù),只寫入20個之后就要讀取,在讀取時limit就會設(shè)置為20。
position:當(dāng)前所操作元素所在索引位置。
position從0開始,隨著get和put方法自動更新。
mark:用來暫時保存position的值。
position保存到mark之后就可以修改并進行相關(guān)的操作,操作完成后可以通過reset方法將mark的值恢復(fù)到position。
mark默認值為-1,且其值必須小于position的值。
例如,Buffer中一共保存了20個數(shù)據(jù),position為10,現(xiàn)在想讀取15到20之間的數(shù)據(jù),這時就可以調(diào)用Buffer的mark方法將目前的position保存到mark中,然后調(diào)用Buffer的position(15)將position指向第15個元素,這時就可以讀取。讀取完成之后使用Buffer的reset就可以將position恢復(fù)到10.
如果調(diào)用Buffer的position方法時傳入的值小于mark當(dāng)前的值,則會將mark設(shè)為-1。
這四個屬性大小關(guān)系:mark<=position<=limit<=capacity
我們將前面的普通Socket示例的服務(wù)端改寫一下:
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; 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.util.Iterator; /** * @author sanchan * @since 1.0 */ public class NIOServer { public static void main(String[] args) { /** * 啟動監(jiān)聽 * 當(dāng)監(jiān)聽到請求時根據(jù)SelectionKey的操作類型交給內(nèi)部類Handler進行處理 */ try { //創(chuàng)建ServerSocketChannel ServerSocketChannel ssc = ServerSocketChannel.open(); //設(shè)置監(jiān)聽8080端口 ssc.socket().bind(new InetSocketAddress(8080)); //設(shè)置為非阻塞模式 ssc.configureBlocking(false); //為ServerSocketChannel注冊Selector Selector selector = Selector.open(); ssc.register(selector, SelectionKey.OP_ACCEPT); //創(chuàng)建Handler Handler handler = new Handler(1024); while (true) { //等待請求,每次等待阻塞3s,超過3秒后線程繼續(xù)運行,如果傳入0或使用無參重載方法,將一直阻塞 if (selector.select(3000) == 0) { System.out.println("等待請求超時~~~~~"); continue; } System.out.println("處理請求~~~~~"); //獲取等待處理的SelectionKey Iterator我是廣告keyIterator = selector.selectedKeys().iterator(); while (keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); try { //根據(jù)不同請求操作選擇對應(yīng)的處理方法 if (key.isAcceptable()) { handler.handleAccept(key); } if (key.isReadable()) { handler.handleRead(key); } } catch (IOException e) { e.printStackTrace(); //如果異常就說明連接結(jié)束,移除 keyIterator.remove(); continue; } } } } catch (IOException e) { e.printStackTrace(); } finally { } } /** * 請求處理類 */ private static class Handler { private int bufferSize = 1024; private String localCharset = "UTF-8"; public Handler() { } public Handler(int bufferSize) { this(bufferSize, null); } public Handler(String localCharset) { this(-1, localCharset); } public Handler(int bufferSize, String localCharset) { if (bufferSize > 0) this.bufferSize = bufferSize; if (localCharset != null) this.localCharset = localCharset; } /** * 處理請求操作 * * @param key * @throws IOException */ public void handleAccept(SelectionKey key) throws IOException { //獲取Channel SocketChannel sc = ((ServerSocketChannel) key.channel()).accept(); //設(shè)置非阻塞 sc.configureBlocking(false); //注冊讀操作的Selector sc.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize)); } /** * 處理讀操作 * * @param key * @throws IOException */ public void handleRead(SelectionKey key) throws IOException { //獲取Channel SocketChannel sc = ((SocketChannel) key.channel()); //獲取ByteBuffer /** * Buffer專門用于存儲數(shù)據(jù),有四個極為重要的屬性: * 1. capacity:容量。 * Buffer最多可以保存元素的數(shù)量,創(chuàng)建時設(shè)置,使用過程中不可修改。 * 2. limit:可以使用的上限。 * 剛創(chuàng)建Buffer時limit等于capacity。如果給limit設(shè)置【不能超過capacity】之后,limit就成了最大可訪問的值。 * 例如,一個Buffer的capacity為100,表示最多可以保存100個數(shù)據(jù),只寫入20個之后就要讀取,在讀取時limit就會設(shè)置為20。 * 3. position:當(dāng)前所操作元素所在索引位置。 * position從0開始,隨著get和put方法自動更新。 * 4. mark:用來暫時保存position的值。 * position保存到mark之后就可以修改并進行相關(guān)的操作,操作完成后可以通過reset方法將mark的值恢復(fù)到position。 * mark默認值為-1,且其值必須小于position的值。 * 例如,Buffer中一共保存了20個數(shù)據(jù),position為10,現(xiàn)在想讀取15到20之間的數(shù)據(jù),這時就可以調(diào)用Buffer的mark方法將目前的position保存到mark中,然后調(diào)用Buffer的position(15)將position指向第15個元素,這時就可以讀取。讀取完成之后使用Buffer的reset就可以將position恢復(fù)到10. * 如果調(diào)用Buffer的position方法時傳入的值小于mark當(dāng)前的值,則會將mark設(shè)為-1。 */ ByteBuffer buffer = (ByteBuffer) key.attachment(); //重置ByteBuffer。設(shè)置limit=capacity、position=0、mark=-1 buffer.clear(); //沒有獲取到內(nèi)容則關(guān)閉 if (sc.read(buffer) == -1) { sc.close(); } else { /** * flip()作用: * 在保存數(shù)據(jù)時保存一個數(shù)據(jù)position加1,保存完成后要讀取數(shù)據(jù) * 就得設(shè)置limit=position,position=0 **/ buffer.flip(); //返回數(shù)據(jù)到客戶端 String receivedString = Charset.forName(localCharset).newDecoder().decode(buffer).toString(); System.out.println("從客戶端獲取到了數(shù)據(jù):" + receivedString); String sendString = "服務(wù)端已經(jīng)獲取到了數(shù)據(jù):" + receivedString; buffer = ByteBuffer.wrap(sendString.getBytes(localCharset)); sc.write(buffer); //關(guān)閉SocketChannel sc.close(); } } } }
本人的直播課程在 7 月份就要開始了,希望小伙伴們支持一下,現(xiàn)在報名有優(yōu)惠噢
https://segmentfault.com/l/15...
https://segmentfault.com/l/15...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/67234.html
摘要:作為一個程序員,不了解內(nèi)存模型就不能寫出能夠充分利用內(nèi)存的代碼。程序計數(shù)器是在電腦處理器中的一個寄存器,用來指示電腦下一步要運行的指令序列。在虛擬機中,本地方法棧和虛擬機棧是共用同一塊內(nèi)存的,不做具體區(qū)分。 作為一個 Java 程序員,不了解 Java 內(nèi)存模型就不能寫出能夠充分利用內(nèi)存的代碼。本文通過對 Java 內(nèi)存模型的介紹,讓讀者能夠了解 Java 的內(nèi)存的分配情況,適合 Ja...
摘要:相等判斷符介紹相等判斷符用于比較基本數(shù)據(jù)類型和引用類型數(shù)據(jù)當(dāng)比較基本數(shù)據(jù)類型的時候比較的是數(shù)值當(dāng)比較引用類型數(shù)據(jù)時比較的是引用指針判斷基本類型是否相等首先基本數(shù)據(jù)類型指的是中的八大數(shù)據(jù)類型這八大基本數(shù)據(jù)類型有個共同的特點是它們在內(nèi)存中是有具相等判斷符==介紹 ? ==相等判斷符用于比較基本數(shù)據(jù)類型和引用類型數(shù)據(jù). 當(dāng)比較基本數(shù)據(jù)類型的時候比較的是數(shù)值, 當(dāng)比較引用類型數(shù)據(jù)時比較的是引用(指...
摘要:相等判斷符介紹相等判斷符用于比較基本數(shù)據(jù)類型和引用類型數(shù)據(jù)當(dāng)比較基本數(shù)據(jù)類型的時候比較的是數(shù)值當(dāng)比較引用類型數(shù)據(jù)時比較的是引用指針判斷基本類型是否相等首先基本數(shù)據(jù)類型指的是中的八大數(shù)據(jù)類型這八大基本數(shù)據(jù)類型有個共同的特點是它們在內(nèi)存中是有具相等判斷符==介紹 ==相等判斷符用于比較基本數(shù)據(jù)類型和引用類型數(shù)據(jù). 當(dāng)比較基本數(shù)據(jù)類型的時候比較的是數(shù)值, 當(dāng)比較引用類型數(shù)據(jù)時比較的是引用(指針...
摘要:相等判斷符介紹相等判斷符用于比較基本數(shù)據(jù)類型和引用類型數(shù)據(jù)當(dāng)比較基本數(shù)據(jù)類型的時候比較的是數(shù)值當(dāng)比較引用類型數(shù)據(jù)時比較的是引用指針判斷基本類型是否相等首先基本數(shù)據(jù)類型指的是中的八大數(shù)據(jù)類型這八大基本數(shù)據(jù)類型有個共同的特點是它們在內(nèi)存中是有具相等判斷符==介紹 ==相等判斷符用于比較基本數(shù)據(jù)類型和引用類型數(shù)據(jù). 當(dāng)比較基本數(shù)據(jù)類型的時候比較的是數(shù)值, 當(dāng)比較引用類型數(shù)據(jù)時比較的是引用(指針...
閱讀 2348·2021-11-15 11:37
閱讀 2625·2021-09-23 11:21
閱讀 2951·2021-09-07 10:11
閱讀 3164·2019-08-30 15:53
閱讀 2826·2019-08-29 15:13
閱讀 1606·2019-08-26 13:57
閱讀 1098·2019-08-26 12:23
閱讀 2438·2019-08-26 11:51