和風(fēng)細(xì)雨

          世上本無難事,心以為難,斯乃真難。茍不存一難之見于心,則運(yùn)用之術(shù)自出。

          Socket基礎(chǔ)

          本文內(nèi)容

          Socket概述
          Socket的重要API
          一個(gè)Socket通信的例子

          Socket是什么?

          Socket通常也稱作“套接字”,用于描述IP地址和端口,是一個(gè)通信鏈的句柄。應(yīng)用程序通常通過“套接字”向網(wǎng)絡(luò)發(fā)出請(qǐng)求或者應(yīng)答網(wǎng)絡(luò)請(qǐng)求。
          在java中,Socket和ServerSocket類庫位于java.net包中。ServerSocket用于服務(wù)器端,Socket是建立網(wǎng)絡(luò)連接時(shí)使用的。在連接成功時(shí),應(yīng)用程序兩端都會(huì)產(chǎn)生一個(gè)Socket實(shí)例,操作這個(gè)實(shí)例,完成所需的會(huì)話。
          對(duì)于一個(gè)網(wǎng)絡(luò)連接來說,套接字是平等的,并沒有差別,不因?yàn)樵诜?wù)器端或在客戶端而產(chǎn)生不同級(jí)別。

          Socket的直觀描述

          Socket的英文原義是“孔”或“插座”。在這里作為進(jìn)程通信機(jī)制,取后一種意義。socket非常類似于電話插座。以一個(gè)國家級(jí)電話網(wǎng)為例。電話的通話雙方相當(dāng)于相互通信的2個(gè)進(jìn)程,區(qū)號(hào)是它的網(wǎng)絡(luò)地址;區(qū)內(nèi)一個(gè)單位的交換機(jī)相當(dāng)于一臺(tái)主機(jī),主機(jī)分配給每個(gè)用戶的局內(nèi)號(hào)碼相當(dāng)于socket號(hào)。任何用戶在通話之前,首先要占有一部電話機(jī),相當(dāng)于申請(qǐng)一個(gè)socket;同時(shí)要知道對(duì)方的號(hào)碼,相當(dāng)于對(duì)方有一個(gè)固定的socket。然后向?qū)Ψ綋芴?hào)呼叫,相當(dāng)于發(fā)出連接請(qǐng)求(假如對(duì)方不在同一區(qū)內(nèi),還要撥對(duì)方區(qū)號(hào),相當(dāng)于給出網(wǎng)絡(luò)地址)。對(duì)方假如在場(chǎng)并空閑(相當(dāng)于通信的另一主機(jī)開機(jī)且可以接受連接請(qǐng)求),拿起電話話筒,雙方就可以正式通話,相當(dāng)于連接成功。雙方通話的過程,是一方向電話機(jī)發(fā)出信號(hào)和對(duì)方從電話機(jī)接收信號(hào)的過程,相當(dāng)于向socket發(fā)送數(shù)據(jù)和從socket接收數(shù)據(jù)。通話結(jié)束后,一方掛起電話機(jī)相當(dāng)于關(guān)閉socket,撤消連接。
          在電話系統(tǒng)中,一般用戶只能感受到本地電話機(jī)和對(duì)方電話號(hào)碼的存在,建立通話的過程,話音傳輸?shù)倪^程以及整個(gè)電話系統(tǒng)的技術(shù)細(xì)節(jié)對(duì)他都是透明的,這也與socket機(jī)制非常相似。socket利用網(wǎng)間網(wǎng)通信設(shè)施實(shí)現(xiàn)進(jìn)程通信,但它對(duì)通信設(shè)施的細(xì)節(jié)毫不關(guān)心,只要通信設(shè)施能提供足夠的通信能力,它就滿足了。
           至此,我們對(duì)socket進(jìn)行了直觀的描述。抽象出來,socket實(shí)質(zhì)上提供了進(jìn)程通信的端點(diǎn)。進(jìn)程通信之前,雙方首先必須各自創(chuàng)建一個(gè)端點(diǎn),否則是沒有辦法建立聯(lián)系并相互通信的。正如打電話之前,雙方必須各自擁有一臺(tái)電話機(jī)一樣。

          socket 是面向客戶/服務(wù)器模型而設(shè)計(jì)的

          socket 是面向客戶/服務(wù)器模型而設(shè)計(jì)的,針對(duì)客戶和服務(wù)器程序提供不同的socket 系統(tǒng)調(diào)用。客戶隨機(jī)申請(qǐng)一個(gè)socket (相當(dāng)于一個(gè)想打電話的人可以在任何一臺(tái)入網(wǎng)電話上撥號(hào)呼叫),系統(tǒng)為之分配一個(gè)socket號(hào);服務(wù)器擁有全局公認(rèn)的 socket ,任何客戶都可以向它發(fā)出連接請(qǐng)求和信息請(qǐng)求(相當(dāng)于一個(gè)被呼叫的電話擁有一個(gè)呼叫方知道的電話號(hào)碼)。
          socket利用客戶/服務(wù)器模式巧妙地解決了進(jìn)程之間建立通信連接的問題。服務(wù)器socket為全局所公認(rèn)非常重要。讀者不妨考慮一下,兩個(gè)完全隨機(jī)的用戶進(jìn)程之間如何建立通信?假如通信雙方?jīng)]有任何一方的socket 固定,就好比打電話的雙方彼此不知道對(duì)方的電話號(hào)碼,要通話是不可能的。

          Socket的應(yīng)用

          Socket 接口是訪問 Internet 使用得最廣泛的方法。 如果你有一臺(tái)剛配好TCP/IP協(xié)議的主機(jī),其IP地址是202.120.127.201, 此時(shí)在另一臺(tái)主機(jī)或同一臺(tái)主機(jī)上執(zhí)行ftp 202.120.127.201,顯然無法建立連接。因"202.120.127.201" 這臺(tái)主機(jī)沒有運(yùn)行FTP服務(wù)軟件。同樣, 在另一臺(tái)或同一臺(tái)主機(jī)上運(yùn)行瀏覽軟件 如Netscape,輸入"http://202.120.127.201",也無法建立連接。現(xiàn)在,如果在這臺(tái)主機(jī)上運(yùn)行一個(gè)FTP服務(wù)軟件(該軟件將打開一個(gè)Socket, 并將其綁定到21端口),再在這臺(tái)主機(jī)上運(yùn)行一個(gè)Web 服務(wù)軟件(該軟件將打開另一個(gè)Socket,并將其綁定到80端口)。這樣,在另一臺(tái)主機(jī)或同一臺(tái)主機(jī)上執(zhí)行ftp 202.120.127.201,F(xiàn)TP客戶軟件將通過21端口來呼叫主機(jī)上由FTP 服務(wù)軟件提供的Socket,與其建立連接并對(duì)話。而在netscape中輸入"http://202.120.127.201"時(shí),將通過80端口來呼叫主機(jī)上由Web服務(wù)軟件提供的Socket,與其建 立連接并對(duì)話。 在Internet上有很多這樣的主機(jī),這些主機(jī)一般運(yùn)行了多個(gè)服務(wù)軟件,同時(shí)提供幾種服務(wù)。每種服務(wù)都打開一個(gè)Socket,并綁定到一個(gè)端口上,不同的端口對(duì)應(yīng)于不同的服務(wù)。Socket正如其英文原意那樣,象一個(gè)多孔插座。一臺(tái)主機(jī)猶如布滿各種插座的房間,每個(gè)插座有一個(gè)編號(hào),有的插座提供220伏交流電, 有的提供110伏交流電,有的則提供有線電視節(jié)目。 客戶軟件將插頭插到不同編號(hào)的插座,就可以得到不同的服務(wù)。

          重要的Socket API

          accept方法用于產(chǎn)生“阻塞”,直到接受到一個(gè)連接,并且返回一個(gè)客戶端的Socket對(duì)象實(shí)例。“阻塞”是一個(gè)術(shù)語,它使程序運(yùn)行暫時(shí)“停留”在這個(gè)地方,直到一個(gè)會(huì)話產(chǎn)生,然后程序繼續(xù);通常“阻塞”是由循環(huán)產(chǎn)生的。
          getInputStream方法獲得網(wǎng)絡(luò)連接輸入,同時(shí)返回一個(gè)IutputStream對(duì)象實(shí)例。
          getOutputStream方法連接的另一端將得到輸入,同時(shí)返回一個(gè)OutputStream對(duì)象實(shí)例。 注意:其中g(shù)etInputStream和getOutputStream方法均會(huì)產(chǎn)生一個(gè)IOException,它必須被捕獲,因?yàn)樗鼈兎祷氐牧鲗?duì)象,通常都會(huì)被另一個(gè)流對(duì)象使用。

          一個(gè)Server-Client模型的程序的開發(fā)原理

          服務(wù)器,使用ServerSocket監(jiān)聽指定的端口,端口可以隨意指定(由于1024以下的端口通常屬于保留端口,在一些操作系統(tǒng)中不可以隨意使用,所以建議使用大于1024的端口),等待客戶連接請(qǐng)求,客戶連接后,會(huì)話產(chǎn)生;在完成會(huì)話后,關(guān)閉連接。
          客戶端,使用Socket對(duì)網(wǎng)絡(luò)上某一個(gè)服務(wù)器的某一個(gè)端口發(fā)出連接請(qǐng)求,一旦連接成功,打開會(huì)話;會(huì)話完成后,關(guān)閉Socket。客戶端不需要指定打開的端口,通常臨時(shí)的、動(dòng)態(tài)的分配一個(gè)1024以上的端口。

          服務(wù)器端代碼

          public class ResponseThread implements Runnable {
            private static Logger logger = Logger.getLogger(ResponseThread.class);
           
            // 用于與客戶端通信的Socket
            private Socket incomingSocket;

            /**
             * 構(gòu)造函數(shù),用以將incomingSocket傳入
             * @param incomingSocket
             */
            public ResponseThread(Socket incomingSocket) {
              this.incomingSocket = incomingSocket;
            }

            public void run() {
              try {
                try {
                  // 輸入流
                  InputStream inStream = incomingSocket.getInputStream();
                 
                  // 輸出流
                  OutputStream outStream = incomingSocket.getOutputStream();

                  // 文本掃描器
                  Scanner in = new Scanner(inStream);
                 
                  // 輸出流打印器
                  PrintWriter out = new PrintWriter(outStream,true);
                 
                  while (in.hasNextLine()) {
                    String line = in.nextLine();
                    logger.info("從客戶端獲得文字:"+line);
                   
                    String responseLine=line+" 門朝大海 三河合水萬年流";
                    out.println(responseLine);
                    logger.info("向客戶端送出文字:"+responseLine);
                  }
                } finally {
                  incomingSocket.close();
                }
              } catch (IOException ex) {
                ex.printStackTrace();
              }
            }
           public static void main(String[] args) {
              try {
                // 建立一個(gè)對(duì)2009端口進(jìn)行監(jiān)聽的ServerSocket并開始監(jiān)聽
                ServerSocket socket=new ServerSocket(2009);
                logger.info("開始監(jiān)聽");
               
                while(true){
                  // 產(chǎn)生阻塞,直到客戶端連接過來才會(huì)往下執(zhí)行
                  logger.info("阻塞中,等待來自客戶端的連接請(qǐng)求");
                  Socket incomingSocket=socket.accept();       
                 
                  // 執(zhí)行到這里客戶端已經(jīng)連接過來了,incomingSocket就是創(chuàng)建出來和遠(yuǎn)程客戶端Socket進(jìn)行通信的Socket
                  logger.info("獲得來自"+incomingSocket.getInetAddress()+"的請(qǐng)求."); 
                 
                  // 開辟一個(gè)線程,并把incomingSocket傳進(jìn)去,在這個(gè)線程中服務(wù)器和客戶機(jī)開始通信
                  ResponseThread responseThread=new ResponseThread(incomingSocket);
                 
                  // 啟動(dòng)線程
                  Thread thread=new Thread(responseThread);
                  thread.start();
                }
               
              } catch (IOException ex) {
                ex.printStackTrace();
              }
            }
          }

          客戶端代碼

          /**
           * Socket客戶端類
           * @author sitinspring
           *
           * @date 2008-2-26
           */
          public class SocketClient{
            private static Logger logger = Logger.getLogger(ResponseThread.class);
           
            public static void main(String[] args){
              try {
                // 建立一個(gè)Socket,試圖連接到位于127.0.0.1的主機(jī)的2009端口,如果服務(wù)器已經(jīng)在監(jiān)聽則會(huì)接收到這個(gè)請(qǐng)求,accept方法產(chǎn)生一個(gè)和這邊通信的Socket
                Socket socket=new Socket("127.0.0.1",2009);
                logger.info("客戶端向服務(wù)器發(fā)起請(qǐng)求.");
               
                try {
                  // 輸入流
                  InputStream inStream = socket.getInputStream();
                 
                  // 輸出流
                  OutputStream outStream = socket.getOutputStream();

                  /**
                   * 向服務(wù)器發(fā)送文字
                   */
                  // 輸出流打印器
                  PrintWriter out = new PrintWriter(outStream);
                  out.println("地震高崗 一派溪山千古秀");
                  out.flush();
                 
                  /**
                   * 接收服務(wù)器發(fā)送過來的文字
                   */
                  // 文本掃描器
                  Scanner in = new Scanner(inStream);
                  while (in.hasNextLine()) {
                    String line = in.nextLine();
                    logger.info("客戶端獲得響應(yīng)文字="+ line);
                  }
                } finally {
                  // 關(guān)閉Socket
                  socket.close();
                }
              } catch (IOException ex) {
                ex.printStackTrace();
              }
            }
          }

          posted on 2008-02-26 13:53 和風(fēng)細(xì)雨 閱讀(443) 評(píng)論(0)  編輯  收藏 所屬分類: J2SE

          主站蜘蛛池模板: 巴楚县| 安西县| 阜平县| 探索| 佛坪县| 建阳市| 五指山市| 彰化市| 布尔津县| 华亭县| 罗平县| 南溪县| 博爱县| 宁城县| 靖州| 林芝县| 宜昌市| 牡丹江市| 新河县| 汕尾市| 扶余县| 昭苏县| 鄱阳县| 巴南区| 松阳县| 普定县| 乌海市| 鹰潭市| 德昌县| 景宁| 鄂伦春自治旗| 堆龙德庆县| 长治市| 瓦房店市| 凤台县| 黄梅县| 漯河市| 舒城县| 峡江县| 铜鼓县| 温州市|