Java網(wǎng)絡編程 - 03 基于TCP協(xié)議的網(wǎng)絡通信

導讀目錄
  • ServerSocket
  • Socket
  • 半關閉的Socket

TCP/IP是一種可靠的網(wǎng)絡協(xié)議骨望,它在通信的兩端各建立一個Socket令漂,從而在通信的兩端之間行形成網(wǎng)絡虛擬鏈路。該虛擬鏈路一旦建立成功鳍寂,兩端的程序就可以通過虛擬鏈路進行通信拌蜘。

通過使用IP協(xié)議彼棍,從而使Internet成為一個允許連接不同類型的計算機和操作系統(tǒng)的網(wǎng)絡。

IP協(xié)議負責將消息從一個主機傳送到另一個主機跷乐,消息在傳送過程中被分割成一個個小包(IP數(shù)據(jù)包)鸿摇,該協(xié)議只是保證了計算計之間可以發(fā)送和接受數(shù)據(jù),但無法解決數(shù)據(jù)分組在傳輸過程中可能出現(xiàn)的問題劈猿,因此拙吉,還需要安裝TCP協(xié)議來提供可靠并且無差錯的通信服務,

TCP協(xié)議被稱為一種端對端協(xié)議揪荣,它會讓兩臺計算計之間建立一個連接:用于發(fā)送和接受數(shù)據(jù)的虛擬鏈路筷黔。TCP協(xié)議負責收集這些信息包,并將其安適當?shù)拇涡蚍藕脗魉驼叹保邮斩耸艿胶笤賹⑵湔_的還原佛舱,TCP協(xié)議保證了數(shù)據(jù)包在傳送中準確無誤。

Java使用Socket對象來代表兩端的通信端口挨决,并通過Socket產(chǎn)生IO流來進行網(wǎng)絡通信

1.使用ServerSocket創(chuàng)建TCP服務器端

在客戶/服務器通信模式中, 服務器端需要創(chuàng)建監(jiān)聽端口的 ServerSocket, ServerSocket 負責接收客戶連接請求

異常類型
在了解Socket的內(nèi)容之前请祖,先要了解一下涉及到的一些異常類型。以下四種類型都是繼承于IOException脖祈,所以很多之后直接彈出IOException即可肆捕。
UnkownHostException //主機名字或IP錯誤
ConnectException //服務器拒絕連接、服務器沒有啟動盖高、(超出隊列數(shù)慎陵,拒絕連接)
SocketTimeoutException //連接超時
BindException //Socket對象無法與制定的本地IP地址或端口綁定

ServerSocket的構造器:
ServerSocket() throws IOException;
ServerSocket(int port) throws IOException;//用指定的端口來創(chuàng)建一個ServerSocket, port的有效值:0~65535
ServerSocket(int port, int backlog) throws IOException;//增加一個用于改變連接隊列長度的參數(shù)backlog
ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException;//在機器上存在多個IP地址的情況下眼虱,允許通過bindAddr參數(shù)來將ServerSocket綁定到指定的IP地址。

注意點:

  1. port服務端是要監(jiān)聽的端口席纽;backlog客戶端連接請求的隊列長度捏悬;bindAddr服務端綁定IP
  2. 如果端口被占用或者沒有權限使用某些端口會拋出BindException錯誤。譬如1~1023的端口需要管理員才擁有權限綁定润梯。
  3. 如果設置端口為0过牙,則系統(tǒng)會自動為其分配一個端口;
  4. bindAddr用于綁定服務器IP纺铭,為什么會有這樣的設置呢寇钉,譬如有些機器有多個網(wǎng)卡。
  5. ServerSocket一旦綁定了監(jiān)聽端口彤蔽,就無法更改摧莽。ServerSocket()可以實現(xiàn)在綁定端口前設置其他的參數(shù)。

ServerSocket的方法:
Socket accept(); //如果接受到一個客戶端Socket的連接請求顿痪,該方法會返回一個與客戶端Socket對應的Socket;否則該方法將會一直處于等待狀態(tài)镊辕,線程也會阻塞(即該方法所在的線程)
void close(); //關閉該Socket
注意:accept()方法是ServerSocket的方法,Socket沒有該方法

2.使用Socket創(chuàng)建客戶端

構造器
Socket();//創(chuàng)建一個默認使用本地主機的默認IP,系統(tǒng)動態(tài)分配的端口的Socket

//創(chuàng)建連接到指定遠程主機蚁袭、端口的Socket,區(qū)別只是IP的傳入方式
Socket(InetAddress address, int port)throws UnknownHostException, IOException
Socket(String host, int port)throws UnknownHostException, IOException

//創(chuàng)建連接到指定遠程主機和遠程端口的Socket征懈,并指定本地IP地址和本地端口,適用于本地主機有多個IP地址的情形
Socket(InetAddress address, int port, InetAddress localAddress, int localPort)throws IOException
Socket(String host, int port, InetAddress localAddress, int localPort)throws IOException

Socket方法
InetAddress getInetAddress();//獲取遠程服務端的IP地址
int getPort();//獲取遠程服務端的端口

InetAddress getLocalAddress();//獲取本地客戶端的IP地址
int getLocalPort();//獲取本地客戶端的端口
void connect(SocketAddress endpoint); //
void connect(SocketAddress endpoint, int timeout)

**InputStream getInputStream();//獲得輸入流 **
**OutputStream getOutputStream();//獲得輸出流 **

值得注意的是揩悄,在這些方法里面卖哎,最重要的就是getInputStream()和getOutputStream()了。

Socket狀態(tài)
boolean isClosed();//連接是否已關閉删性,若關閉亏娜,返回true;否則返回false
boolean isConnect();//如果曾經(jīng)連接過蹬挺,返回true维贺;否則返回false
boolean isBound();//如果Socket已經(jīng)與本地一個端口綁定,返回true巴帮;否則返回false

如果要確認Socket的狀態(tài)是否處于連接中溯泣,下面語句是很好的判斷方式。
boolean isConnection = socket.isConnected() && !socket.isClosed();//判斷當前是否處于連接

*例子
服務器端

import java.net.ServerSocket;
import java.net.Socket;
import java.io.*;
public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(3006);
        while(true) {
            System.out.println("等待連接...");
            Socket client = ss.accept();
            System.out.println("已有客戶端連接成功榕茧,正在接受消息...");
            BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));//接受
            String line;
            while((line = br.readLine()) != null) {
                if("exit".equals(line)) {
                    client.close();
                }else {
                    System.out.println("對方發(fā)來的消息:" + line);
                }
            }
        }
    }
}

客戶端

import java.net.Socket;
import java.net.InetAddress;
import java.io.*;
public class Client {
    public static void main(String[] args) throws IOException {     
        Socket s = new Socket(InetAddress.getLocalHost(), 3006);//由于服務器端是在本地垃沦,故這里使用InetAddress.getLocalHost()    
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String line;
        PrintStream ps = new PrintStream(s.getOutputStream());//因為發(fā)送的是文本內(nèi)容,采用這個打印流的用押,要是其他文件最好使用字節(jié)輸出流肢簿,程序更有健壯性
        while((line = br.readLine()) != null) {
            ps.println(line);
        }
    }
}
3.半關閉的Socket

在IO流中,如果要表示輸出已經(jīng)結束,則可以通過關閉輸出流來實現(xiàn)译仗,但在網(wǎng)絡通信中則不能通過關閉輸出流來表示輸出已經(jīng)結束抬虽,因為關閉輸出流官觅,該輸出流對應的Socket也將隨之關閉纵菌,

因此,Socket提供了兩個半關閉的方法休涤,只關閉Socket的輸入流或輸出流咱圆,用以表示輸出數(shù)據(jù)已經(jīng)發(fā)送完成,
void shutdownInput(); //關閉Socket的輸入流功氨,該Socket還可以輸出數(shù)據(jù), 進入半讀狀態(tài)
void shutdownOutput(); //關閉該Socket的輸出流序苏,該Socket還可以輸入數(shù)據(jù), 進入半寫狀態(tài)

調(diào)用這兩個方法后的關閉Sockets的輸入流或輸出流,該Socket處于"半關閉"狀態(tài)
注意:即使先后調(diào)用這兩個方法也不會關閉Socket捷凄,只不過處于既不能輸出也不能輸入的狀態(tài)忱详;但是如果關閉Socket對應的輸出流(getOutputStream())或輸入流(getInputStream())關閉之后,就會徹底將Socket關閉跺涤。

判斷該Socket的狀態(tài)
boolean isInputShutdown(); //判斷Socket是否處于半讀狀態(tài)(read-half)
boolean isOutputShutdown(); //判斷Socket是否處于半寫狀態(tài)(write-half)

注意:當Socket關閉了輸入流或輸出流后匈睁,該Socket無法再次打開輸出流或輸入流

(2)半關閉適用于哪些場景:

1)適用于那些不需要長久保持通信狀態(tài)的一站式服務;
2)特別適用于HTTP通信桶错,而且特別適用于客戶端航唆,因為客戶端在發(fā)送請求后就無需再發(fā)送其它數(shù)據(jù)了,往往只是等待服務器端返回想要的資源院刁;
3)因此在客戶端發(fā)送完請求之后就可以立馬半關閉輸出流了(半寫狀態(tài))糯钙;

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市退腥,隨后出現(xiàn)的幾起案子任岸,更是在濱河造成了極大的恐慌,老刑警劉巖狡刘,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件享潜,死亡現(xiàn)場離奇詭異,居然都是意外死亡颓帝,警方通過查閱死者的電腦和手機米碰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來购城,“玉大人吕座,你說我怎么就攤上這事”癜澹” “怎么了吴趴?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長侮攀。 經(jīng)常有香客問我锣枝,道長厢拭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任撇叁,我火速辦了婚禮供鸠,結果婚禮上,老公的妹妹穿的比我還像新娘陨闹。我一直安慰自己楞捂,他們只是感情好,可當我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布趋厉。 她就那樣靜靜地躺著寨闹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪君账。 梳的紋絲不亂的頭發(fā)上繁堡,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天,我揣著相機與錄音乡数,去河邊找鬼椭蹄。 笑死,一個胖子當著我的面吹牛瞳脓,可吹牛的內(nèi)容都是我干的塑娇。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼劫侧,長吁一口氣:“原來是場噩夢啊……” “哼埋酬!你這毒婦竟也來了?” 一聲冷哼從身側響起烧栋,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤写妥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后审姓,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體珍特,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年魔吐,在試婚紗的時候發(fā)現(xiàn)自己被綠了扎筒。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡酬姆,死狀恐怖嗜桌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情辞色,我是刑警寧澤骨宠,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響层亿,放射性物質(zhì)發(fā)生泄漏桦卒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一匿又、第九天 我趴在偏房一處隱蔽的房頂上張望方灾。 院中可真熱鬧,春花似錦琳省、人聲如沸迎吵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拢蛋,卻和暖如春桦他,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谆棱。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工快压, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人垃瞧。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓蔫劣,卻偏偏與公主長得像,于是被迫代替她去往敵國和親个从。 傳聞我的和親對象是個殘疾皇子脉幢,可洞房花燭夜當晚...
    茶點故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容