Android網(wǎng)絡(luò) | Socket(Eclipse--Java)

  • 在現(xiàn)實(shí)網(wǎng)絡(luò)傳輸應(yīng)用中推正,
    通常使用TCP恍涂、IPUDP這3種協(xié)議實(shí)現(xiàn)數(shù)據(jù)傳輸

    傳輸數(shù)據(jù)的過程中植榕,
    需要通過一個(gè)雙向的通信連接實(shí)現(xiàn)數(shù)據(jù)的交互再沧。

    在這個(gè)傳輸過程中,
    通常將這個(gè)雙向鏈路的一端稱為Socket尊残,
    一個(gè)Socket通常由一個(gè)IP地址和一個(gè)端口號(hào)來確定炒瘸。

    在整個(gè)數(shù)據(jù)傳輸過程中Socket的作用是巨大的寝衫。
    在Java編程應(yīng)用中顷扩,Socket是Java網(wǎng)絡(luò)編程的核心。

Socket基礎(chǔ)

  • 網(wǎng)絡(luò)編程中有兩個(gè)主要的問題慰毅,
    一個(gè)是如何準(zhǔn)確地定位網(wǎng)絡(luò)上一臺(tái)或多臺(tái)主機(jī)隘截,
    另一個(gè)就是找到主機(jī)后如何可靠高效地進(jìn)行數(shù)據(jù)傳輸

    TCP/IP協(xié)議
    IP層主要負(fù)責(zé)網(wǎng)絡(luò)主機(jī)的定位汹胃,數(shù)據(jù)傳輸路由婶芭,
    IP地址可以唯一地確定Internet上的一臺(tái)主機(jī)
    TCP層
    提供面向應(yīng)用的可靠(TCP)
    非可靠(UDP)數(shù)據(jù)傳輸機(jī)制着饥,
    這是網(wǎng)絡(luò)編程主要對(duì)象犀农,
    一般不需要關(guān)心IP 層如何處理數(shù)據(jù)的。

    目前較為流行的網(wǎng)絡(luò)編程模型客戶機(jī)/服務(wù)器(C/S)結(jié)構(gòu)宰掉。
    通信雙方井赌,一方作為服務(wù)器
    等待(另一方作為的)客戶提出請(qǐng)求并予以響應(yīng)谤逼。
    客戶則在需要服務(wù)時(shí)向服務(wù)器提出申請(qǐng)

    服務(wù)器一般作為守護(hù)進(jìn)程 始終運(yùn)行仇穗,
    監(jiān)聽網(wǎng)絡(luò)端口流部,
    一旦有客戶請(qǐng)求,就會(huì)啟動(dòng)一個(gè)服務(wù)進(jìn)程來響應(yīng)該客戶纹坐,
    同時(shí)自己繼續(xù)監(jiān)聽服務(wù)端口枝冀,
    使后來的客戶也能及時(shí)得到服務(wù)

TCP/IP協(xié)議基礎(chǔ)

  • TCP/IPTransmission Control Protocol/Internet Protocol的簡(jiǎn)寫耘子,
    中譯名為傳輸控制協(xié)議/因特網(wǎng)協(xié)議果漾,
    又名網(wǎng)絡(luò)通信協(xié)議
    是Internet最基本的協(xié)議谷誓、Internet國(guó)際互聯(lián)網(wǎng)絡(luò)基礎(chǔ)盅安,

    網(wǎng)絡(luò)層IP協(xié)議
    傳輸層TCP協(xié)議組成继找。

    TCP/IP定義了電子設(shè)備如何連入因特網(wǎng)贪婉,
    以及數(shù)據(jù)如何在它們之間傳輸?shù)臉?biāo)準(zhǔn)恨狈。

    TCP/IP協(xié)議采用了4層層級(jí)結(jié)構(gòu)
    每一層都呼叫它的下一層所提供的協(xié)議來完成自己的需求糙臼。

    也就是說庐镐,
    TCP負(fù)責(zé)發(fā)現(xiàn)傳輸的問題,
    一旦發(fā)現(xiàn)問題便發(fā)出信號(hào)要求重新傳輸变逃,
    直到所有數(shù)據(jù)安全正確地傳輸?shù)侥康牡?/code>必逆。
    IP的功能是給因特網(wǎng)的每一臺(tái)電腦規(guī)定一個(gè)地址

    TCP/IP協(xié)議不是TCPIP這兩個(gè)協(xié)議的合稱揽乱,
    而是指因特網(wǎng)整個(gè)TCP/IP協(xié)議簇名眉。

    協(xié)議分層模型方面來講,TCP/IP4個(gè)層次組成凰棉,
    分別是網(wǎng)絡(luò)接口層损拢、網(wǎng)絡(luò)層傳輸層渊啰、應(yīng)用層探橱。

  • 其實(shí)TCP/IP協(xié)議并不完全符合OSI(Open System Interconnect)7層參考模型申屹,
    OSI是傳統(tǒng)的開放式系統(tǒng)互連參考模型绘证,
    是一種通信協(xié)議7層抽象參考模型
    其中每一層執(zhí)行某一特定任務(wù)哗讥。
    該模型的目的
    使各種硬件相同的層次相互通信嚷那。

    7層
    物理層、數(shù)據(jù)鏈路層(網(wǎng)絡(luò)接口層)杆煞、
    網(wǎng)絡(luò)層(網(wǎng)絡(luò)層)魏宽、
    傳送層(傳輸層)腐泻、
    會(huì)話層、表示層和應(yīng)用層(應(yīng)用層)队询。
    TCP/IP協(xié)議采用了4層的層級(jí)結(jié)構(gòu)派桩,
    每一層都呼叫它的下一層所提供的網(wǎng)絡(luò)來完成自己的需求

    由于ARPANET的設(shè)計(jì)者注重的是網(wǎng)絡(luò)互聯(lián)蚌斩,
    允許通信子網(wǎng)(網(wǎng)絡(luò)接口層)采用已有的或是將來有的各種協(xié)議铆惑,
    所以這個(gè)層次中沒有提供專門的協(xié)議

    實(shí)際上送膳,
    TCP/IP協(xié)議可以通過網(wǎng)絡(luò)接口層連接到任何網(wǎng)絡(luò)上员魏,
    例如X.25交換網(wǎng)IEEE802局域網(wǎng)

UDP協(xié)議

  • UDPUser Datagram Protocol的簡(jiǎn)稱叠聋,
    是一種無連接的協(xié)議撕阎,
    每個(gè)數(shù)據(jù)報(bào)都是一個(gè)獨(dú)立的信息
    包括完整源地址目的地址碌补,
    它在網(wǎng)絡(luò)上以任何可能的路徑傳往目的地虏束,
    因此能否到達(dá)目的地
    到達(dá)目的地的時(shí)間以及內(nèi)容的正確性都是不能被保證的脑慧。

    現(xiàn)實(shí)網(wǎng)絡(luò)數(shù)據(jù)傳輸過程中魄眉,
    大多數(shù)功能是由TCP協(xié)議UDP協(xié)議實(shí)現(xiàn)。

  • (1)TCP協(xié)議
    面向連接的協(xié)議闷袒,
    Socket之間進(jìn)行數(shù)據(jù)傳輸之前必然要建立連接坑律,
    所以在TCP中需要連接時(shí)間

    TCP傳輸數(shù)據(jù)大小限制囊骤,
    一旦連接建立起來晃择,
    雙方的Socket就可以按統(tǒng)一的格式傳輸大的數(shù)據(jù)
    TCP是一個(gè)可靠的協(xié)議也物,
    它確保接收方完全正確地獲取發(fā)送方所發(fā)送的全部數(shù)據(jù)宫屠。

  • (2)UDP協(xié)議
    每個(gè)數(shù)據(jù)報(bào)中都給出了完整地址信息
    因此無需要建立發(fā)送方接收方連接滑蚯。

    UDP傳輸數(shù)據(jù)時(shí)是有大小限制的浪蹂,
    每個(gè)被傳輸?shù)臄?shù)據(jù)報(bào)必須限定在64KB之內(nèi)。

    UDP是一個(gè)不可靠的協(xié)議告材,
    發(fā)送方所發(fā)送的數(shù)據(jù)報(bào)并不一定以相同的次序到達(dá)接收方坤次。

TCP、UDP選擇的決定因素
  • (1)TCP在網(wǎng)絡(luò)通信上有極強(qiáng)生命力斥赋,
    例如遠(yuǎn)程連接(Telnet)文件傳輸(FTP)
    都需要不定長(zhǎng)度數(shù)據(jù)可靠地傳輸缰猴。
    但是可靠的傳輸是要付出代價(jià)的,
    對(duì)數(shù)據(jù)內(nèi)容正確性的檢驗(yàn)必然占用計(jì)算機(jī)的處理時(shí)間網(wǎng)絡(luò)的帶寬疤剑,
    因此TCP傳輸效率不如UDP高滑绒。

  • (2)UDP 操作簡(jiǎn)單闷堡,而且僅需要較少的監(jiān)護(hù)
    因此通常用于局域網(wǎng)高可靠性分散系統(tǒng)Client/Server 應(yīng)用程序疑故。
    例如視頻會(huì)議系統(tǒng)杠览,
    并不要求音頻視頻數(shù)據(jù) 絕對(duì)的正確
    只要保證連貫性就可以了纵势,
    這種情況下顯然使用UDP會(huì)更合理一些倦零,
    因?yàn)?code>TCP
    UDP都能達(dá)到這個(gè)保證連貫性的門檻,
    但是TCP卻要多占用更多的計(jì)算機(jī)資源吨悍,
    殺雞焉用牛刀呢扫茅,
    所有這種情況不用TCP,用UDP育瓜。

基于Socket的Java網(wǎng)絡(luò)編程

  • 網(wǎng)絡(luò)上的兩個(gè)程序通過一個(gè)雙向通信連接實(shí)現(xiàn)數(shù)據(jù)的交換葫隙,
    這個(gè)雙向鏈路一端稱為一個(gè)Socket

  • Socket通常用來實(shí)現(xiàn)客戶方服務(wù)方的連接躏仇。

  • SocketTCP/IP協(xié)議的一個(gè)十分流行的編程方式恋脚,
    一個(gè)Socket一個(gè)IP地址一個(gè)端口號(hào) 唯一確定
    但是焰手,
    Socket所支持的協(xié)議種類也不光TCP/IP一種糟描,
    因此兩者之間是沒有必然聯(lián)系的。
    Java環(huán)境下书妻,
    Socket編程主要是指基于TCP/IP協(xié)議網(wǎng)絡(luò)編程船响。

1.Socket通信的過程
  • ServerListen(監(jiān)聽)某個(gè)端口是否有連接請(qǐng)求
    Client端Server 端發(fā)出Connect(連接)請(qǐng)求躲履,
    Server端Client端發(fā)回Accept(接收)消息见间,
    一個(gè)連接就建立起來了。

  • Server端Client端都可以通過Send工猜、Write等方法與對(duì)方通信米诉。

  • Java網(wǎng)絡(luò)編程應(yīng)用中,
    對(duì)于一個(gè)功能齊全的Socket來說篷帅,
    其工作過程包含如下所示的基本步驟史侣。
    (1)創(chuàng)建ServerSocketSocket
    (2)打開連接到Socket輸入/輸出流魏身;
    (3)按照一定的協(xié)議對(duì)Socket進(jìn)行讀/寫操作惊橱;
    (4)關(guān)閉IO流Socket

2.創(chuàng)建Socket
  • Java網(wǎng)絡(luò)編程應(yīng)用中叠骑,
    java.net中提供了兩個(gè)類SocketServerSocket李皇,
    分別用來表示雙向連接客戶端服務(wù)端削茁。
    這是兩個(gè)封裝得非常好的類宙枷,
    其中包含了如下所示的構(gòu)造方法

  • Socket(InetAddress address, int port)掉房;

  • Socket(InetAddress address, int port, boolean stream);

  • Socket(String host, int prot)慰丛;

  • Socket(String host, int prot, boolean stream)卓囚;

  • Socket(SocketImpl impl);

  • Socket(String host, int port, InetAddress localAddr, int localPort)诅病;

  • Socket(InetAddress address, int port, InetAddress localAddr, int localPort)哪亿;

  • ServerSocket(int port);

  • ServerSocket(int port, int backlog)贤笆;

  • ServerSocket(int port, int backlog, InetAddress bindAddr)

  • 在上述構(gòu)造方法中蝇棉,
    參數(shù)addresshostport分別是
    雙向連接另一方IP地址芥永、主機(jī)名端口號(hào)篡殷,
    stream指明Socket流Socket還是數(shù)據(jù)報(bào)Socket
    localPort表示本地主機(jī)端口號(hào)埋涧,
    localAddrbindAddr本地機(jī)器的地址ServerSocket主機(jī)地址)板辽,
    implSocket父類
    既可以用來創(chuàng)建ServerSocket又可以用來創(chuàng)建Socket棘催。

    例如:

    Socket client = new Socket("127.0.0.1", 80);
    ServerSocket server = new ServerSocket(80);
  • 注意:
    必須小心地選擇端口劲弦,
    每一個(gè)端口提供一種特定的服務(wù)
    只有給出正確的端口醇坝,才能獲得相應(yīng)的服務(wù)邑跪。

    0~1023端口號(hào)系統(tǒng)所保留
    例如
    HTTP服務(wù)的端口號(hào)為80,
    Telnet服務(wù)的端口號(hào)為21,
    FTP服務(wù)的端口號(hào)為23呼猪,
    所以我們?cè)?code>選擇端口號(hào)時(shí)呀袱,最好選擇一個(gè)大于1023數(shù)以防止發(fā)生沖突
    另外郑叠,
    創(chuàng)建Socket時(shí)如果發(fā)生錯(cuò)誤夜赵,將產(chǎn)生IOException
    程序中必須對(duì)之做出處理乡革。
    所以在創(chuàng)建SocketServerSocket時(shí)必須捕獲拋出異常寇僧。

TCP編程詳解

  • TCP/IP通信協(xié)議是一種可靠網(wǎng)絡(luò)協(xié)議
    能夠在通信的兩端各建立一個(gè)Socket沸版,
    從而在通信的兩端之間形成網(wǎng)絡(luò)虛擬鏈路嘁傀。

    一旦建立了虛擬的網(wǎng)絡(luò)鏈路
    兩端的程序就可以通過虛擬鏈路進(jìn)行通信视粮。

    Java語(yǔ)言對(duì)TCP網(wǎng)絡(luò)通信提供了良好的封裝细办,
    通過Socket對(duì)象代表兩端通信端口
    并通過Socket產(chǎn)生的IO流進(jìn)行網(wǎng)絡(luò)通信

    這里先筆記Java應(yīng)用中TCP編程的基本知識(shí)笑撞,
    為后面的Android編程打下基礎(chǔ)岛啸。
使用ServerSocket

在Java程序中,

  • 使用
    類ServerSocket 接受其他通信實(shí)體連接請(qǐng)求茴肥。
    對(duì)象ServerSocket的功能是監(jiān)聽來自客戶端的Socket連接坚踩,
    如果沒有連接則會(huì)一直處于等待狀態(tài)

  • 在類ServerSocket中包含了如下監(jiān)聽客戶端連接請(qǐng)求的方法:
    Socket accept():如果接收到一個(gè)客戶端Socket連接請(qǐng)求瓤狐,
    該方法將返回一個(gè)與客戶端Socket對(duì)應(yīng)的Socket瞬铸,
    否則該方法將一直處于等待狀態(tài),線程也被阻塞础锐。

  • 為了創(chuàng)建ServerSocket對(duì)象嗓节,
    ServerSocket類為我們提供了如下構(gòu)造器

    • ServerSocket(int port)
      用指定的端口port創(chuàng)建一個(gè)ServerSocket
      端口應(yīng)該是有一個(gè)有效端口整數(shù)值0~65535皆警。

    • ServerSocket(int port,int backlog)
      增加一個(gè)用來改變連接隊(duì)列長(zhǎng)度參數(shù)backlog赦政。

    • ServerSocket(int port,int backlog,InetAddress localAddr)
      機(jī)器(服務(wù)器、本機(jī)等)存在多個(gè)IP地址的情況下耀怜,
      允許通過localAddr這個(gè)參數(shù)
      來指定將ServerSocket綁定到指定的IP地址恢着。

  • 當(dāng)使用ServerSocket后,
    需要使用ServerSocket中的方法close()關(guān)閉該ServerSocket财破。

  • 在通常情況下掰派,
    因?yàn)榉?wù)器不會(huì)只接受一個(gè)客戶端請(qǐng)求
    而是會(huì)不斷地接受來自客戶端所有請(qǐng)求左痢,
    所以可以通過循環(huán)不斷調(diào)用ServerSocket中的方法accept()靡羡。

  • 例如下面的代碼。

    //創(chuàng)建一個(gè)ServerSocket俊性,用于監(jiān)聽客戶端Socket的連接請(qǐng)求
    ServerSocket ss = new ServerSocket(30000);
    //采用循環(huán)不斷接受來自客戶端的請(qǐng)求
    while (true)
    {
    //每當(dāng)接受到客戶端Socket的請(qǐng)求略步,服務(wù)器端也對(duì)應(yīng)產(chǎn)生一個(gè)Socket
    Socket s = ss.accept();
    //下面就可以使用Socket進(jìn)行通信了
    ...
    }
  • 在上述代碼中定页,
    創(chuàng)建的ServerSocket沒有指定IP地址杭煎,
    ServerSocket會(huì)綁定本機(jī)默認(rèn)IP地址

    在代碼中使用30000作為該ServerSocket端口號(hào)也切,
    通常推薦使用10000以上的端口
    主要是為了避免與其他應(yīng)用程序通用端口 沖突导犹。
使用Socket
  • 在客戶端可以使用Socket構(gòu)造器
    實(shí)現(xiàn)``和指定服務(wù)器連接卷雕,
    Socket中可以使用如下兩個(gè)構(gòu)造器:

    • Socket(InetAddress/String remoteAddress, int port)
      創(chuàng)建連接到指定遠(yuǎn)程主機(jī)遠(yuǎn)程端口的Socket
      該構(gòu)造器沒有指定本地地址本地端口锥咸,
      本地IP地址端口使用默認(rèn)值缔刹。

    • Socket(InetAddress/String remoteAddress, int port, InetAddress localAddr, int localPort)
      創(chuàng)建連接到指定遠(yuǎn)程主機(jī)鸟廓、遠(yuǎn)程端口Socket
      并指定本地IP地址本地端口號(hào)
      適用于本地主機(jī)多個(gè)IP地址的情形。

  • 在使用上述構(gòu)造器指定遠(yuǎn)程主機(jī)時(shí),
    既可使用InetAddress來指定绘搞,也可以使用String對(duì)象指定楼雹,
    Java中通常使用String對(duì)象指定遠(yuǎn)程IP块茁,例如192.168.2.23
    當(dāng)本地主機(jī)只有一個(gè)IP地址時(shí)崎场,建議使用第一個(gè)方法谭跨,簡(jiǎn)單方便。
    例如下面的代碼:

    //創(chuàng)建連接到本機(jī)状土、30000端口的Socket
    Socket s = new Socket("127.0.0.1" , 30000);
  • 當(dāng)程序執(zhí)行上述代碼后會(huì)連接到指定服務(wù)器
    服務(wù)器端ServerSocket的方法accept()向下執(zhí)行泻肯,
    于是服務(wù)器端客戶端就產(chǎn)生一對(duì)互相連接的Socket琉朽。
    上述代碼連接到“遠(yuǎn)程主機(jī)”的IP地址是127.0.0.1
    IP地址總是代表本機(jī)的IP地址稚铣。
    這里例程的服務(wù)器端箱叁、客戶端都是在本機(jī)運(yùn)行,
    所以Socket連接到遠(yuǎn)程主機(jī)IP地址使用127.0.0.1惕医。

  • 當(dāng)客戶端耕漱、服務(wù)器端產(chǎn)生對(duì)應(yīng)的Socket之后,
    程序無須再區(qū)分服務(wù)器端和客戶端抬伺,
    而是通過各自的Socket進(jìn)行通信螟够。

  • Socket中提供如下兩個(gè)方法獲取輸入流輸出流

    • InputStream getInputStream()
      返回該Socket對(duì)象 對(duì)應(yīng)的輸入流
      讓程序通過該輸入流從Socket中取出數(shù)據(jù)峡钓。

    • OutputStream getOutputStream()
      返回該 Socket對(duì)象 對(duì)應(yīng)的輸出流妓笙,
      讓程序通過該輸出流Socket輸出數(shù)據(jù)

  • TCP協(xié)議的服務(wù)器端例程:

public class Server
{
    public static void main(String[] args)
        throws IOException
    {
        //創(chuàng)建一個(gè)ServerSocket能岩,用于監(jiān)聽客戶端Socket的連接請(qǐng)求
        ServerSocket myss = new ServerSocket(30001);
        //采用循環(huán)不斷接受來自客戶端的請(qǐng)求
        while (true)
        {
          //每當(dāng)接受到客戶端Socket的請(qǐng)求寞宫,服務(wù)器端也對(duì)應(yīng)產(chǎn)生一個(gè)Socket
          Socket s = myss.accept();
          //將Socket對(duì)應(yīng)的輸出流包裝成PrintStream
          PrintStream ps = new PrintStream(s.getOutputStream());
          //進(jìn)行普通IO操作
          ps.println("凌川江雪!");
          ps.println("望川霄云拉鹃!");
          ps.println("萬(wàn)年太久淆九,只爭(zhēng)朝夕统锤!");
          ps.println("人間正道是滄桑!");
          ps.println("窮善其身炭庙,達(dá)濟(jì)天下饲窿!");
          
          //關(guān)閉輸出流,關(guān)閉Socket
          ps.close();
          s.close();
        }
    }
}
  • 上述代碼建立了ServerSocket監(jiān)聽焕蹄,
    并且使用Socket獲取了輸出流逾雄,
    執(zhí)行后不會(huì)顯示任何信息。
  • 對(duì)應(yīng)的TCP協(xié)議的客戶端例程:
public class Client {

    public static void main(String[] args)  throws IOException
    {
        Socket socket = new Socket("127.0.0.1" , 30001);
        //將Socket對(duì)應(yīng)的輸入流包裝成BufferedReader
        BufferedReader br = new BufferedReader(
          new InputStreamReader(socket.getInputStream()));
        
        //進(jìn)行普通IO操作         
        StringBuilder response = new StringBuilder();
        String line;
        
        //一行一行地讀取并加進(jìn)stringbuilder
        while((line = br.readLine()) != null){
            response.append(line + "\n");
        }
        
        System.out.println("來自服務(wù)器的數(shù)據(jù):" + "\n" + response.toString());  
        
        //關(guān)閉輸入流腻脏、Socket
        br.close();
        socket.close();
    }

}
  • 上述代碼使用Socket建立了與指定IP鸦泳、指定端口的連接,
    并使用Socket獲取輸入流讀取數(shù)據(jù)永品,
    之后處理一下數(shù)據(jù)然后打印在工作臺(tái)做鹰。
    運(yùn)行服務(wù)端Class運(yùn)行客戶端Class鼎姐,運(yùn)行結(jié)果:

  • 由此可見钾麸,
    一旦使用ServerSocketSocket建立網(wǎng)絡(luò)連接之后,
    程序通過網(wǎng)絡(luò)通信普通IO并沒有太大的區(qū)別炕桨。
    如果先運(yùn)行上面程序中的Server 類饭尝,
    將看到服務(wù)器一直處于等待狀態(tài)
    因?yàn)榉?wù)器使用了死循環(huán)來接受來自客戶端的請(qǐng)求献宫;
    再運(yùn)行Client類钥平,
    將可看到程序輸出“來自服務(wù)器的數(shù)據(jù):...!”姊途,
    這表明客戶端和服務(wù)器端通信成功涉瘾。





TCP中的多線程

  • 剛剛實(shí)操的例程中,
    ServerClient只是進(jìn)行了簡(jiǎn)單的通信操作捷兰,
    當(dāng)服務(wù)器接收到客戶端連接之后立叛,服務(wù)器向客戶端輸出一個(gè)字符串,
    客戶端也只是讀取服務(wù)器的字符串退出了寂殉。

  • 在實(shí)際應(yīng)用中囚巴,
    客戶端可能需要和服務(wù)器端保持長(zhǎng)時(shí)間通信
    服務(wù)器需要不斷讀取客戶端數(shù)據(jù)友扰,
    向客戶端寫入數(shù)據(jù)彤叉,
    客戶端也需要不斷讀取服務(wù)器數(shù)據(jù),
    向服務(wù)器寫入數(shù)據(jù)村怪。

  • 當(dāng)使用readLine()方法讀取數(shù)據(jù)時(shí)秽浇,
    如果在該方法成功返回之前線程阻塞,則程序無法繼續(xù)執(zhí)行甚负。
    所以服務(wù)器很有必要為每個(gè)Socket 單獨(dú)啟動(dòng)一條線程柬焕,
    每條線程 負(fù)責(zé)與一個(gè)客戶端進(jìn)行通信审残。

  • 另外,
    因?yàn)?code>客戶端讀取服務(wù)器數(shù)據(jù)線程同樣會(huì)被阻塞斑举,
    所以系統(tǒng)應(yīng)該單獨(dú) 啟動(dòng)一條線程搅轿,
    該組線程專門負(fù)責(zé)讀取服務(wù)器數(shù)據(jù)

  • 假設(shè)要開發(fā)一個(gè)聊天室程序富玷,
    服務(wù)器端應(yīng)該包含多條線程璧坟,
    其中每個(gè)Socket對(duì)應(yīng)一條線程

    線程負(fù)責(zé)
    讀取 Socket 對(duì)應(yīng)輸入流數(shù)據(jù)
    (從客戶端發(fā)送過來的數(shù)據(jù))赎懦,

    并將讀到的數(shù)據(jù)
    每個(gè)Socket輸出流發(fā)送一遍
    (將一個(gè)客戶端 發(fā)送的數(shù)據(jù) “廣播”其他客戶端 )雀鹃;

  • 因此需要在服務(wù)器端使用List保存所有的Socket
    具體實(shí)現(xiàn)時(shí)励两,
    服務(wù)器提供了如下兩個(gè)類
    創(chuàng)建ServerSocket監(jiān)聽主類黎茎。
    處理每個(gè)Socket通信線程類

1/4 接下來介紹具體實(shí)現(xiàn)流程当悔,首先看下面的IServerClass:

public class IServer
{
    //定義保存所有Socket的ArrayList
    public static ArrayList<Socket> socketList = new ArrayList<Socket>();

    public static void main(String[] args) 
        throws IOException
    {
        ServerSocket ss = new ServerSocket(30000);
        while(true)
        {
            //此行代碼會(huì)阻塞傅瞻,將一直等待別人的連接
            Socket s = ss.accept();
            socketList.add(s);
            //每當(dāng)客戶端連接后啟動(dòng)一條ServerThread線程為該客戶端服務(wù)
            new Thread(new Serverxian(s)).start();
        }
    }
}

IServer類中,
服務(wù)器端(ServerSocket )只負(fù)責(zé)接受客戶端Socket連接請(qǐng)求先鱼,
每當(dāng)客戶端Socket連接到該ServerSocket之后俭正,
程序?qū)?code>客戶端對(duì)應(yīng)的Socket(客戶Socket的對(duì)面一端)加入 socketList集合保存奸鬓,
并為該Socket啟動(dòng)一條線程Serverxian)焙畔,
線程負(fù)責(zé)處理 該Socket所有 的 通信任務(wù)
小結(jié):
IServer類完成的業(yè)務(wù)是:
1.接收客戶端Socket串远,
2.保存對(duì)應(yīng)返回的Socket宏多,
3.啟動(dòng)處理線程

2/4 接著看服務(wù)器端線程類文件:

package liao.server;
import java.io.*;
import java.net.*;
import java.util.*;

//負(fù)責(zé)處理每個(gè)線程通信的線程類
public class Serverxian implements Runnable 
{
    //定義當(dāng)前線程所處理的Socket
    Socket s = null;
    
    //該線程所處理的Socket所對(duì)應(yīng)的輸入流讀取器
    BufferedReader br = null;
    
    public Serverxian(Socket s)
        throws IOException
    {
        this.s = s;
        //初始化該Socket對(duì)應(yīng)的輸入流
        br = new BufferedReader(new InputStreamReader(s.getInputStream()));
    }
    
    public void run()
    {
        try
        {
            String content = null;
            
            //采用循環(huán)不斷從Socket中讀取客戶端發(fā)送過來的數(shù)據(jù)
            while ((content = readFromClient()) != null)
            {
                
                //遍歷socketList中的每個(gè)Socket澡罚,
                //將讀到的內(nèi)容向每個(gè)Socket發(fā)送一次
                for (Socket s : IServer.socketList)
                {
                     //將Socket對(duì)應(yīng)的輸出流包裝成PrintStream
                    PrintStream ps = new PrintStream(s.getOutputStream());
                    ps.println(content);
                }
            }
        }
        catch (IOException e)
        {
            //e.printStackTrace();
        }
    }
    
    //定義讀取客戶端數(shù)據(jù)的方法
    private String readFromClient()
    {
        try
        {
            return br.readLine();   
        }
        //如果捕捉到異常伸但,表明該Socket對(duì)應(yīng)的客戶端已經(jīng)關(guān)閉
        catch (IOException e)
        {
            //刪除該Socket。
            IServer.socketList.remove(s);
        }
        return null;
    }
}

Serverxian類(服務(wù)器端線程類)中留搔,
注意是線程類更胖,繼承Runnable,重寫run方法
會(huì)不斷讀取客戶端數(shù)據(jù)隔显,

在獲取時(shí)使用方法readFromClient()來讀取客戶端數(shù)據(jù)却妨。
如果讀取數(shù)據(jù)過程中捕獲到 IOException異常,
則說明此Socket對(duì)應(yīng)的客戶端Socket出現(xiàn)了問題括眠,
程序就會(huì)將此Socket從socketList中刪除彪标。

當(dāng)服務(wù)器線程讀到客戶端數(shù)據(jù)之后會(huì)遍歷整個(gè)socketList集合,
并將該數(shù)據(jù)向socketList集合中的每個(gè)Socket發(fā)送一次掷豺,
該服務(wù)器線程將把從Socket中讀到的數(shù)據(jù)
向socketList中的每個(gè)Socket轉(zhuǎn)發(fā)一次捞烟。

  • 上述代碼能夠不斷獲取Socket輸入流中的內(nèi)容薄声,
    當(dāng)獲取Socket輸入流中的內(nèi)容后,
    直接將這些內(nèi)容打印在控制臺(tái)题画。

    先運(yùn)行上面程序中的類IServer默辨,
    該類運(yùn)行后作為本應(yīng)用的服務(wù)器,不會(huì)看到任何輸出苍息。接著可以運(yùn)行多個(gè) IClient——相當(dāng)于啟動(dòng)多個(gè)聊天室客戶端登錄該服務(wù)器廓奕,此時(shí)在任何一個(gè)客戶端通過鍵盤輸入一些內(nèi)容后單擊“回車”鍵,將可看到所有客戶端(包括自己)都會(huì)在控制臺(tái)收到剛剛輸入的內(nèi)容档叔,這就簡(jiǎn)單實(shí)現(xiàn)了一個(gè)聊天室的功能桌粉。

  • 運(yùn)行結(jié)果如下動(dòng)圖所示:
    (這個(gè)鏈接是
    在Eclipse上,同時(shí)運(yùn)行多個(gè)java程序衙四,
    用不同的console顯示運(yùn)行信息的方法)

    圖中展現(xiàn)的是:已經(jīng)啟動(dòng)服務(wù)端铃肯,
    同時(shí)啟動(dòng)兩個(gè)客戶端,
    來回切換客戶端進(jìn)行“聊天”传蹈,
    客戶端由于服務(wù)端的socket傳輸押逼,
    可以相互收到彼此的信息;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末惦界,一起剝皮案震驚了整個(gè)濱河市挑格,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌沾歪,老刑警劉巖漂彤,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異灾搏,居然都是意外死亡挫望,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門狂窑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來媳板,“玉大人,你說我怎么就攤上這事泉哈◎刃遥” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵丛晦,是天一觀的道長(zhǎng)奕纫。 經(jīng)常有香客問我,道長(zhǎng)采呐,這世上最難降的妖魔是什么若锁? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮斧吐,結(jié)果婚禮上又固,老公的妹妹穿的比我還像新娘仲器。我一直安慰自己,他們只是感情好仰冠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布乏冀。 她就那樣靜靜地躺著,像睡著了一般洋只。 火紅的嫁衣襯著肌膚如雪辆沦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天识虚,我揣著相機(jī)與錄音肢扯,去河邊找鬼。 笑死担锤,一個(gè)胖子當(dāng)著我的面吹牛蔚晨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肛循,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼铭腕,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了多糠?” 一聲冷哼從身側(cè)響起累舷,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎夹孔,沒想到半個(gè)月后被盈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡析蝴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年害捕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绿淋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闷畸。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖吞滞,靈堂內(nèi)的尸體忽然破棺而出佑菩,到底是詐尸還是另有隱情,我是刑警寧澤裁赠,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布殿漠,位于F島的核電站,受9級(jí)特大地震影響佩捞,放射性物質(zhì)發(fā)生泄漏绞幌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一一忱、第九天 我趴在偏房一處隱蔽的房頂上張望莲蜘。 院中可真熱鬧谭确,春花似錦、人聲如沸票渠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)问顷。三九已至昂秃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間杜窄,已是汗流浹背肠骆。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留塞耕,地道東北人哗戈。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像荷科,于是被迫代替她去往敵國(guó)和親唯咬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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

  • 對(duì)于即時(shí)類應(yīng)用或者即時(shí)類的游戲畏浆,HTTP協(xié)議很多時(shí)候無法滿足于我們的需求胆胰。這會(huì),Socket對(duì)于我們來說就非常實(shí)用...
    育樹凌峰閱讀 1,996評(píng)論 0 1
  • 網(wǎng)絡(luò)編程的概述 網(wǎng)絡(luò)編程的實(shí)質(zhì)就是用來實(shí)現(xiàn)網(wǎng)絡(luò)互連的不同計(jì)算機(jī)上運(yùn)行的程序間可以進(jìn)行數(shù)據(jù)交換刻获。 一.OSI網(wǎng)絡(luò)模型...
    思念揮霍閱讀 377評(píng)論 0 0
  • 【JAVA 網(wǎng)絡(luò)編程 TCP/IP蝎毡、Socket 和協(xié)議設(shè)計(jì)】 轉(zhuǎn)自:TCP/IP厚柳、Socket 和協(xié)議設(shè)計(jì) TC...
    Rtia閱讀 3,636評(píng)論 0 5
  • 喂~唔~ 都可以隨便的,你說的我都愿意去沐兵,像小火車擺動(dòng)的旋律…… 在屯門看到小火車别垮,對(duì)常去香港購(gòu)物的深圳市民來說,...
    蘆瘋子閱讀 929評(píng)論 5 15
  • 之前已經(jīng)闡述過靜脈曲張的大致樣子扎谎、臨床表現(xiàn)碳想、帶給人們的危害、以及部分認(rèn)識(shí)誤區(qū)毁靶。那么胧奔,我們到底該如何防治這種發(fā)病率非...
    蘇磊CHIVA閱讀 502評(píng)論 0 0