Java課程設計專案例項《遠端螢幕分享監視》第1部分
1、專案相關程式實現的基本原理
本專案由於採用C/S模式(客戶機/伺服器)的系統架構實現方案,其中的伺服器端主機也就是被分享(被監視)螢幕的主機(只有一臺主機),而客戶機就是顯示出被分享螢幕的主機(監視主機可以是多臺),從而形成一臺伺服器但多個客戶機的應用狀況(參看如下示例圖所示的網路連線模式)。
程式實現的原理其實也很簡單,在被監視的主機上(也就是本專案中的SocketServer伺服器端),針對每個成功連線的客戶端(也就是本專案中的SocketClient客戶端)執行一個執行緒,每隔一段時間就自動截圖產生出對應的影象資料,並把截圖資料進行壓縮後再發送到指定的監視主機中;而在監視主機中,也需要同步執行一個執行緒,接收從被監視的主機發送來的影象資料的壓縮包,解壓所獲得的壓縮包,最終獲得截圖的影象資料,並繪製到監視主機的螢幕上,從而顯示出被監視的螢幕。
也就是將當前被監視的主機螢幕的顯示內容捕捉為影象,然後將捕捉的影象資料傳送到遠端控制主機中,最後遠端監視主機接收影象資料並在本機螢幕中顯示出。當然,其中還需要應用多執行緒技術實現自動迴圈捕獲、傳送和接收、顯示以達到實時更新的應用效果。如下示例圖為本專案執行後的某個時刻截圖。
遠端螢幕監視系統應用很廣泛,學校機房的機房管理系統、PC版QQ的遠端演示功能、線上教學的遠端演示等都屬於遠端螢幕監視系統。而如果要再進一步實現遠端控制,則必須要有監控端與被監控端,而且兩端的程式都要保持啟動。在監控端的監視窗體上執行滑鼠點選事件,並記錄滑鼠點選的座標位置及鍵值,然後再發送到被監控端。被監控端程式接受傳送來的滑鼠座標位置及鍵值等資訊,然後再在本地螢幕上模擬同樣的點選動作以響應監控端程式的操作。
2、與網路通訊相關的Java核心類ServerSocket和Secket類
(1)套接字Socket及java.net.Socket類
網路中雙向通訊中的某一端稱為一個Socket(套接字是對雙向通訊中的端點的抽象),但在Java系統中分為客戶端套接字java.net.Socket類和伺服器端套接字java.net.ServerSocket類。Socket通訊的基本原理如下:Socket可以看成在兩個程式進行通訊連線中的一個端點,一個程式將一段資訊寫入Socket中,該Socket將這段資訊傳送給另外一個Socket,從而也就使得這段資訊能傳送到其它程式中;而基於Socket通訊的主要過程包括:Socket的建立、監聽、連線、傳送資料和接收資料等環節。
由於伺服器端的程式可能會對外提供多個不同型別的服務,如何標識和區分不同的服務?在生活中的服務機構中一般是透過不同的視窗號加以區分,而在網路通訊中的伺服器系統則以不同的埠號加以標識和區分。因此,網路通訊中的埠號主要是區分服務類別(伺服器端所提供的功能)。
在Java平臺下的網路通訊系統中的埠號反映在程式中是一個16位無符號整數,數值範圍是0-65535。低於256的埠號保留給標準的應用程式服務所使用,比如WWW服務的埠號為80,telnet服務的埠號為21,ftp服務的埠號為23,smtp服務的埠號為25,pop3服務的埠號為110等。
(2)java.net.ServerSocket類
在客戶/伺服器通訊模式中,伺服器端需要建立監聽特定埠的ServerSocket類的物件例項,ServerSocket類的物件例項主要負責監聽並接收客戶端的連線請求。監聽則必須要有一個目標,這主要是透過埠來區分。但如果ServerSocket類的物件例項所監聽的埠已經被其它的伺服器程式的程序所佔用,從而導致無法繫結到此埠,此時將會丟擲IOException異常。
因此,在伺服器端程式中首先要創建出監聽特定埠的ServerSocket物件(參看如下示例程式碼),從而使得ServerSocket能夠負責接收客戶端的連線請求。
ServerSocket serverSocket = null;int monitoredPortNo = 3721;serverSocket = new ServerSocket(monitoredPortNo);
在伺服器端所接收到的每個客戶機的連線請求都會儲存在一個先進先出的佇列中,而這個佇列的容量是有限的。只有當伺服器端的程式程序透過ServerSocket類中的accept()方法從佇列中取出某個Socket連線請求後,儲存連線請求的佇列才能繼續加入新的客戶端的Socket連線請求。前面的程式程式碼示例所建立的ServerSocket類的物件例項相關的佇列長度為系統預設的長度,可以採用如下的示例程式碼建立指定佇列長度(也就是伺服器可以接受請求的客戶端Socket的最大數量)的ServerSocket類的物件例項。
ServerSocket serverSocket = null;int monitoredPortNo = 3721;int maxQueueLength =100;serverSocket = new ServerSocket(monitoredPortNo, maxQueueLength);
3、在伺服器端程式中迴圈監聽客戶端請求連線的狀況
伺服器端程序成功繫結待監聽的某個埠後,由於客戶端的連線請求是隨時都可能產生的,伺服器端程式則必須要一直不斷地查詢是否產生了連線請求。因此,在程式設計方面就需要應用一個while迴圈語句不斷地執行ServerSocket.accept()方法(參看如下程式碼示例),該方法將從連線請求的佇列中取出成功連線到伺服器的Socket物件。然後再應用Socket.getInetAddress()方法可以查詢獲得成功連線到伺服器的客戶端連線地址以識別客戶;當然,如果此時的連線請求佇列中沒有新的客戶產生的連線請求,accept()方法將需要再次查詢,從而進入一直監聽等待的狀況,直到接收到了新的連線請求才再次返回。
ServerSocket類的accept()方法從連線請求佇列中取出成功連線到伺服器的某個客戶的連線請求,然後再創建出與客戶連線的Socket物件,並將它返回。每當某個客戶機成功連線到伺服器程式後,就為該客戶建立一個執行緒,同時將代表該客戶的Socket傳入到該執行緒中,由該執行緒負責該客戶端的所有互動行為。這樣的程式設計實現機制將使得伺服器端程式能夠產生“一對多”的應用效果——也就是一個伺服器端程式可以同時響應多個不同的客戶端程式的請求和服務互動。工作原理參看如下示例圖所示。
4、伺服器程式需要指定待繫結的埠和所在的主機IP地址
在應用ServerSocke類構建出ServerSocke類的物件例項時,如果沒有指定目標IP地址(此時意味著伺服器主機只有一個IP地址),則在預設的情況下,伺服器程式就與伺服器程式所在的主機的唯一IP地址繫結(參看前面的示例程式碼)。但如果伺服器主機安裝有兩個網絡卡,從而使得同一個伺服器主機出現有多個不同的IP地址的應用狀況。此時,在建立ServerSocke類的物件例項時需要指定待繫結的埠所在的IP地址(參看如下程式碼示例)。
ServerSocket serverSocket = null;int monitoredPortNo = 3721;int maxClientNumber = 50,String serverIPAddress = "192.168.0.1"serverSocket = new ServerSocket (serverIPAddress , monitoredPortNo);serverSocket = new ServerSocket(monitoredPortNo, maxClientNumber, InetAddress.getByName(serverIPAddress));
5、如何獲得伺服器主機及客戶機的IP地址
在網路通訊中的每一臺主機必須要加以區分,為此需要採用一種唯一的標識,這是透過IP地址(Internet Protocol Address,網際網路協議地址)加以實現。IP地址可以識別網路中的各個主機,IP地址是一個邏輯地址,它必須要具有唯一性。在Java程式中如何獲得伺服器主機及客戶機的IP地址?這可以透過程式設計和應用InetAddress和Socket類加以實現。
在Java程式中是透過應用InetAddress類的物件例項表示IP地址,但由於InetAddress類沒有提供public型別的構造方法,因此不能直接應用new 運算子創建出InetAddress類的物件例項,而必須透過InetAddress類中所提供的靜態方法獲取其物件例項。比如InetAddress類的getByName()方法可以根據所給定的主機名稱獲得對應的IP地址。如下示例圖所示的程式程式碼是獲得某個Web伺服器的IP地址。
當然,也可以依據某個IP地址獲得對應的主機名稱。但由於建立InetAddress物件例項的方式不同,透過InetAddress類中的getHostName()方法返回的結果值是不同的。如下示例圖所示的程式程式碼說明了三種不同的方式下它們之間的差別。
在第一種方式下,由於InetAddress物件是用getLocalHost方法建立的,因此getHostName方法返回的卻是本機名而不是遠端主機名;在第二種方式下,用域名作為getByName方法的引數返回InetAddress物件例項後,系統會自動記住這個域名。當再透過呼叫getHostName方法時,就無需再訪問DNS伺服器,而是直接將這個域名返回;而在第三種方式使用IP地址建立InetAddress物件時,則會用DNS反向解析來找到對應的域名/機器名。但如果安全檢查不允許透過IP查詢對應的域名的對映,程式會一直嘗試查詢, 大概10秒的時間,並且在這10秒期間是阻塞的(好像程式宕機的感覺)。getHostName方法最終就直接返回這個IP地址,而不是對應的主機名。
而如果在網路通訊過程中,伺服器端程式希望獲得正在連線的客戶機的IP地址或者客戶端程式希望查詢目標伺服器主機的IP地址,則可以透過Socket類中的相關方法加以實現。
1)透過呼叫Socket類中的getInetAddress()方法可以獲得遠端主機(對方主機)的IP地址、getPort()方法可以獲得遠端主機的埠號。但要注意的是,如果是在伺服器端的程式中呼叫getInetAddress()方法將獲得客戶機的IP地址,而在客戶機程式中呼叫getInetAddress()方法將獲得是伺服器主機的IP地址。
2)透過呼叫Socket類中的getLocalAddress()方法可以獲得本機的IP地址(自己的IP地址)、getLocalPort()方法可以獲得本機的埠號。因此,在伺服器端程式中呼叫getLocalAddress()方法將獲得伺服器主機的IP地址,而在客戶機程式中呼叫getLocalAddress()方法將獲得客戶機自身的IP地址。
6、正確地程式設計迴圈監聽客戶端連線的相關程式併合理地處理SocketException異常
Java的基於TCP的網路程式設計非常簡單化,不再需要開發人員考慮底層的網路TCP/IP協議等複雜的程式設計實現細節。只需要按照透過 Socket獲得輸入輸出流物件,再用標準的 I/O 流的資料讀寫進行資料傳送和接受。在Java程式中實現Socket通訊的程式設計步驟如下:
首先,根據程式的型別不同在伺服器端程式中創建出ServerSocket類物件例項、而在客戶端程式中直接創建出Socket類的物件例項並聯通目標伺服器。
然後雙方利用Socket類中的getInputStream()、getOutputStream()等方法獲得對應的輸入輸出流物件例項,從而可以相互發送和接收資訊(類似於檔案讀寫的過程)。
再其次,伺服器端和客戶端程式根據應用的業務處理規則,雙方讀寫資料(輸入輸出)從而最終實現相互發送和接收資訊。
最後,在雙方的程式中都需要及時地先關閉基於Socket的IO流物件例項,再關閉Socket類的物件以釋放所佔用的系統資源。
但在伺服器端程式與客戶機程式處於通訊的資料傳輸過程中,比如如果伺服器在某個時候正在向某個客戶機發送資料,而此時的客戶端如果斷開了Socket連線(比如直接關閉客戶機程式等情況),那麼伺服器端程式就會丟擲一個IOException類的子類SocketException型別的異常,錯誤資訊為(參看如下示例圖所示):java.net.SocketException: Connection reset by peer: socket write error
在伺服器端程式中必須要捕獲這個異常,因為這個異常的產生只是說明伺服器與某個客戶機程式在通訊過程中出現了異常,而不應該讓這個異常導致伺服器主機程式直接退出,從而影響到伺服器與其它客戶機的通訊過程也被動地終止;此外,一旦出現了這樣的異常,需要及時地將無用的客戶端的Socket物件銷燬,以釋放所佔用的系統資源。
因此,在伺服器端程式中需要正確地程式設計迴圈監聽客戶端連線的程式以合理地處理SocketException型別的異常,如下示例圖所示為相關的程式程式碼。
在程式中由於正確地識別了IOException型別的異常產生的原因,從而也就可以準確地顯示出對應的錯誤提示資訊,提醒和指導使用者的進一步操作過程(參看如下示例圖所示的執行結果)。當然,也可以遮蔽不顯示出原始的異常資訊而只給出更明確的錯誤提示資訊。
7、避免伺服器程式多次繫結某個埠或者埠已經被佔用所造成的異常丟擲
當Socket伺服器程式已經執行,目標服務也已經啟動後,如果此時再次啟動執行伺服器端程式,將會由於需要應用伺服器所繫結的埠,但此時已經被在記憶體中執行的伺服器程式所佔用而丟擲如下示例圖所示的BindException型別的異常,其該異常所對應的錯誤資訊為:java.net.BindException: Address already in use: JVM_Bind。
解決此問題的主要思路是如果能夠在伺服器端程式啟動之前先測試所要繫結的埠是否已經被佔用了,如果此時的埠已經被佔用,則意味著伺服器程式已經啟動,此時就不再需要再次啟動伺服器,而是直接退出當前的程序。實現此功能的示例程式程式碼參看如下示例圖所示的程式程式碼。
該段程式程式碼所反映出的測試原理主要是基於如下的規則:如果可以正常地創建出Socket物件例項,則意味著指定埠上已經啟動了服務,也就意味著可以證明主機上的此目標埠已經被使用(但並非是此Socket所使用的);反之則意味著證明這個埠並沒有由程式使用。
8、應用java.awt.Robot類中的相關方法實現螢幕截圖
java.awt 程式包中的Robot類主要用於為測試自動化、自執行演示程式和其它需要控制滑鼠和鍵盤的應用程式生成本機系統的輸入事件,從而可以在程式中實現自動操作滑鼠和鍵盤。提供Robot類的主要目的是便於在Java系統平臺中實現對程式的自動化測試,而建立Robot類的物件例項也很簡單,就像建立普通Java程式類的物件例項一樣。
首先生成一個Robot類的物件例項(參看如下的程式碼示例),透過這個物件例項呼叫Robot類中的相關方法來操作和控制程式執行時的主機鍵盤和滑鼠、包括擷取螢幕的影象等功能。
Robot currentRobot = new Robot();
其次,透過如下示例所示的兩個方法可以模擬實現按下一個鍵盤的按鍵的應用效果。而其中的int keycode引數代表所要按鍵的鍵碼值。但為什麼每次需要呼叫兩個方法才模擬實現按下一個鍵盤的按鍵?因為在使用者平時按下一個按鍵時,而對於鍵盤來說則其實涉及兩次按鍵行為:一次是按下這個按鍵(也就是將使用者的手指按下不動),第二次是抬起這個按鍵(也就是將使用者的手指抬起)。
currentRobot.keyPress(int keycode);currentRobot.keyRelease(int keycode);
如下的示例程式碼則表示模擬按下鍵盤的Alt + Tab鍵,從而模擬實現切換當前的視窗。
currentRobot.keyPress(KeyEvent.VK_ALT);currentRobot.keyPress(KeyEvent.VK_TAB);currentRobot.keyRelease(KeyEvent.VK_TAB);currentRobot.keyRelease(KeyEvent.VK_ALT);
此外,在Robot類中還提供有擷取螢幕影象的方法createScreenCapture,該方法的定義如下:public BufferedImage createScreenCapture(Rectangle screenRect); 其中的引數screenRect代表需要擷取的螢幕的區域。如果需要擷取主機的整個螢幕,則可以透過Toolkit.getDefaultToolkit().getScreenSize()獲得當前螢幕的尺寸,然後再進行截圖。程式示例請參看如下的程式碼或者如下示例圖中所示的程式程式碼:
Rectangle screenRectangle = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());BufferedImage oneBufferedImage = currentRobot.createScreenCapture(screenRectangle);
因此,在被監視的主機中每隔一段時間就自動截圖產生出對應的影象資料,並把截圖資料進行壓縮後再發送到指定的監視主機中。在監視主機中只需要獲得此截圖的影象資料,然後將此截圖的影象資料轉換回指定格式的圖片,最後再螢幕中顯示。
9、實現BufferedImage所包裝的影象資料和byte型別的陣列之間相互轉換
(1)java.awt.Image類和java.awt.image.BufferedImage類
在Java語言中對影象的抽象描述是由Image類承擔,但由於Image類是抽象類,在實際應用中一般都是透過程式設計其子類BufferedImage。BufferedImage代表某個影象(或者圖片)在記憶體緩衝區中的資料,透過BufferedImage類的的物件例項可以實現對對映到記憶體緩衝區中的資料進行操作,從而最終實現了對應影象(或者圖片)的操作——比如,獲得繪圖物件、對影象縮放、選擇影象平滑度、對影象進行變換、圖片變灰處理、設定透明度等。
(2)BufferedImage由於是非序列化類,在網路上不能直接傳送其物件例項
在網路傳輸中,圖片(影象)格式的資料是不能直接傳送的。因此需要把某種圖片格式的資料轉變為位元組陣列,也就是將包裝影象(或者圖片)的BufferedImage類的物件例項轉換為byte型別的陣列。為此,需要新建一個ByteArrayOutputStream型別的位元組輸出流物件例項,然後再應用ImageIO.write()方法將包裝影象(或者圖片)的BufferedImage類的物件例項輸出到此ByteArrayOutputStream型別的位元組輸出流物件例項中,最後將圖片(影象)格式的資料轉換為位元組陣列。參看如下的示例程式碼或者如下示例圖所示的程式程式碼。
ByteArrayOutputStream oneByteArrayOutputStream = null;oneByteArrayOutputStream = new ByteArrayOutputStream();ImageIO.write(oneBufferedImage, "jpg", oneByteArrayOutputStream);byte[] imageBytesArray = oneByteArrayOutputStream.toByteArray();
而在接收影象的客戶程式中可以採用如下示例所示的程式程式碼根據所獲得的位元組陣列,重新構建出BufferedImage類的物件例項。然後就可以在影象的接收端對影象根據業務的需要進行處理。實現的過程及相關的程式程式碼如下:首先,從基於Socket的物件輸入流中反序列化出所接收到的資訊物件,從而獲得包裝影象的位元組陣列(由imageByteArrayFromServer變數定義)。
byte[] imageByteArrayFromServer = null;ObjectInputStream socketObjectInputStream = new ObjectInputStream(socketGZIPInputStream);MessageInfoPO oneMessageInfoPO = (MessageInfoPO)socketObjectInputStream.readObject();imageByteArrayFromServer =oneMessageInfoPO.getImageBytesArray();
其次,再根據所獲得的包裝影象的位元組陣列,將它轉換為基於位元組資料的輸入流,然後再從位元組資料輸入流中獲得包裝影象的BufferedImage類的物件例項(參看如下示例程式程式碼或者參看如下示例圖所示的程式程式碼)。
ByteArrayInputStream oneByteArrayInputStream = new ByteArrayInputStream(imageByteArrayFromServer);oneBufferedImage = ImageIO.read(oneByteArrayInputStream);
10、對傳輸的可序列化物件進行壓縮以提高資料在網路傳輸中的效率
GZip格式的資料壓縮是目前在IT界廣泛應用的一種資料壓縮方式,它具有很高的壓縮比和壓縮效率。在JDK的系統庫中也包含有java.util.zip程式包提供對GZip格式的資料壓縮和解壓縮的技術支援,從而可以在Java程式中很方便地應用GZip格式的資料壓縮和解壓縮技術。這主要是應用java.util.zip.GZIPInputStream和java.util.zip.GZIPOutputStream兩個流類加以實現。
GZIPInputStream流類的構造方法的定義如下:public GZIPInputStream(InputStream in) throws IOException,因此只需要為GZIPInputStream流類的構造方法傳遞一個輸入流,就可以創建出GZIPInputStream流類的物件例項。
而GZIPOutputStream 流類的構造方法的定義如下:public GZIPOutputStream(OutputStream out) throws IOException,同樣也只需要為GZIPOutputStream 流類的構造方法傳遞一個輸出流,就可以創建出GZIPOutputStream流類的物件例項。
在Socket網路通訊中,為了提高資料在網路上的傳輸效率,可以對需要傳輸的資料(包括可序列化的物件)應用GZIPInputStream和GZIPOutputStream兩個流類實現GZip格式的資料壓縮和解壓縮。比如在遠端螢幕監視的程式中由於需要達到實時監視,因此需要考慮對影象資料傳輸的效率問題,有必要應用資料壓縮技術。
在本專案中,首先,對截圖的影象採用JPG格式的影象進行傳輸。因為JPG格式是一種支援高度壓縮技術的圖片格式,它所儲存的資訊不包含透明度,同等質量的狀況下相對來說其資料容量要比png、gif等格式的圖片容量小;其次,再應用GZip格式的資料壓縮和解壓縮技術對需要傳輸的JPG格式的影象資料進行壓縮,在接收端再解壓縮出對應的影象資料,以進一步提高網路中資料的傳輸效率。
在本專案的伺服器端程式中,直接從Socket物件例項中獲得對應的OutputStream輸出流類的物件例項,然後再將此輸出流類的物件例項包裝轉換為GZIPOutputStream輸出流類的物件例項(參看如下示例圖所示的程式程式碼),從而可以實現對截圖的影象採用GZip格式進行資料壓縮。
而在本專案的客戶端程式中,同樣從Socket物件例項中獲得對應的InputStream輸入流類的物件例項,然後再將此輸入流類的物件例項包裝轉換為GZIPInputStream輸入流類的物件例項(參看如下示例圖所示的程式程式碼),從而可以實現從輸入流中獲得截圖的影象資料,並採用GZip格式進行資料的解壓縮最終獲得對應的影象資料。
11、檢測伺服器端程式是否已經執行,只在啟動了伺服器後客戶端才能連線伺服器
客戶端程式要想與遠端的伺服器進行Socket連線,首先是遠端的伺服器必須要正常執行、對應的服務已經啟動,否則客戶端是無法連線伺服器的,而且還會丟擲如下示例圖所示的ConnectException型別的異常。因此,客戶端程式啟動或者連線伺服器之前,有必要判斷遠端伺服器目前是否已經正常啟動了。如果伺服器目前還沒有啟動,則客戶端程式也就沒必要連線伺服器程式。
在java.net程式包中提供有InetSocketAddress類,該類實現了可序列化Serializable介面並直接繼承自java.net.SocketAddress類。InetSocketAddress類不僅可以實現對IP 套接字地址(IP 地址 + 埠號)的封裝,也還可以實現對主機名套接字(主機名 + 埠號)的封裝。因此,在程式中可以應用“IP 地址 + 埠號”或者“主機名 + 埠號”的引數方式創建出InetSocketAddress類的物件例項。
InetSocketAddress類與InetAddress類在應用方面存在有如下的不同之處:InetAddress類只實現了對IP 地址和主機名的封裝,不包括埠號。在本專案的客戶端程式中為了能夠檢測伺服器是否已經啟動,可以應用InetSocketAddress類的物件例項所包裝的伺服器IP地址和埠號嘗試連線伺服器,然後再測試連線是否成功,從而可以檢測出伺服器目前是否已經啟動。實現此功能的程式程式碼示例可以參看如下示例圖所示的程式程式碼。
客戶端程式在啟動時首先識別伺服器是否已經啟動,如果此時的伺服器沒有啟動,則提示出相關的資訊,並直接退出程序——參看如下示例圖所示。
12、在客戶機中監視伺服器主機是否已經關閉
在網路通訊過程中可能會出現各種各樣的問題,比如伺服器可能由於某種原因而導致宕機或者服務已經退出或關閉等狀況,而此時的客戶程式可能還需要傳送資訊或者嘗試再次連線伺服器,但此時肯定會出現如下示例圖所示的SocketException型別的異常(參看如下示例圖所示)。
因此,有必要檢測伺服器目前是否還處於正常提供服務狀況或者識別伺服器主機是否已經關閉等。功能實現的程式程式碼參看如下示例圖所示的程式程式碼。
13、客戶程式如何識別伺服器是否仍處於活動狀況或者Socket連線是否已經斷開
客戶端程式在傳送資訊之前,需要判斷遠端伺服器目前是否已經斷開了Socket連線,如果斷開了Socket連線,則需要重新連線伺服器以保證資訊傳送的正確性。儘管Socket類中提供有isClosed ()、isConnected ()、isOutputShutdown ()包括isInputShutdown等方法,但這些方法都是反映本地端的狀態(也就是說在客戶端程式中透過Socket物件呼叫這些方法的返回狀態結果都是客戶端程式自己的狀態),而無法判斷遠端伺服器是否已經處於斷開Socket連線的狀態。
但Socket類中提供有sendUrgentData()方法,該方法實現向輸出流傳送一個位元組的資料(儘管該方法的引數為int型別的4個位元組,但只有低8位是有效的),只要對方Socket物件的SO_OOBINLINE屬性沒有開啟(透過 public void setOOBInline(boolean on) throws SocketException 方法可以設定該屬性的狀態),就會自動捨棄這個位元組,而SO_OOBINLINE屬性在預設情況下是關閉的。程式碼示例如下或者參看如下示例圖所示的程式程式碼。