《JAVA:從入門到精通》part 22

二十五、網(wǎng)絡(luò)通信基礎(chǔ)

1. 網(wǎng)絡(luò)程序設(shè)計(jì)基礎(chǔ)

局域網(wǎng)與廣域網(wǎng)

  • 局域網(wǎng)(Local Area Network,LAN)是指在某一區(qū)域內(nèi)由多臺(tái)計(jì)算機(jī)互聯(lián)成的計(jì)算機(jī)組返帕。一般是方圓幾千米以內(nèi)丑慎。局域網(wǎng)可以實(shí)現(xiàn)文件管理、應(yīng)用軟件共享誓斥、打印機(jī)共享、工作組內(nèi)的日程安排许帐、電子郵件和傳真通信服務(wù)等功能劳坑。局域網(wǎng)是封閉型的,可以由辦公室內(nèi)的兩臺(tái)計(jì)算機(jī)組成成畦,也可以由一個(gè)公司內(nèi)的上千臺(tái)計(jì)算機(jī)組成泡垃。


    局域網(wǎng)
  • 廣域網(wǎng)(Wide Area Network,WAN)羡鸥,又稱廣域網(wǎng)蔑穴、外網(wǎng)、公網(wǎng)惧浴。是連接不同地區(qū)局域網(wǎng)或城域網(wǎng)計(jì)算機(jī)通信的遠(yuǎn)程網(wǎng)存和。通常跨接很大的物理范圍衷旅,所覆蓋的范圍從幾十公里到幾千公里捐腿,它能連接多個(gè)地區(qū)、城市和國(guó)家柿顶,或橫跨幾個(gè)洲并能提供遠(yuǎn)距離通信茄袖,形成國(guó)際性的遠(yuǎn)程網(wǎng)絡(luò)。廣域網(wǎng)并不等同于互聯(lián)網(wǎng)嘁锯。


    廣域網(wǎng)

網(wǎng)絡(luò)協(xié)議

  • 網(wǎng)絡(luò)協(xié)議規(guī)定了計(jì)算機(jī)之間連接的物理宪祥、機(jī)械(網(wǎng)線與網(wǎng)卡的連接規(guī)定)聂薪、電氣(有效的電平范圍)等特征,計(jì)算機(jī)之間的互相尋址規(guī)則蝗羊,數(shù)據(jù)發(fā)送沖突的解決方式藏澳,長(zhǎng)數(shù)據(jù)如何分段傳送與接收等內(nèi)容。就像不同的國(guó)家有不同的法律一樣耀找,目前網(wǎng)絡(luò)協(xié)議主要的有TCP協(xié)議和UDP協(xié)議翔悠。

(1)TCP協(xié)議

  • TCP協(xié)議是一種以固接連線為基礎(chǔ)的協(xié)議,它提供兩臺(tái)計(jì)算機(jī)間可靠的數(shù)據(jù)傳送野芒。TCP可以保證從一端數(shù)據(jù)送至連接的另一端蓄愁,數(shù)據(jù)能夠確實(shí)送達(dá),而且抵達(dá)的數(shù)據(jù)的排列順序和送出時(shí)的排列順序相同狞悲。因此TCP協(xié)議適合可靠性要求比較高的場(chǎng)合撮抓。就像撥打電話,必須先撥號(hào)給對(duì)方效诅,等兩端確定連接后胀滚,相互才能聽到對(duì)方說話趟济,也知道對(duì)方回應(yīng)什么乱投。


    TCP協(xié)議
    TCP協(xié)議應(yīng)用
  • HTTP、FTP和Telnet等都需要使用可靠的通信頻道顷编。例如戚炫,HTTP從某個(gè)URL讀取數(shù)據(jù)時(shí),如果收到的數(shù)據(jù)順序與發(fā)送時(shí)不相同媳纬,可能會(huì)出現(xiàn)一個(gè)混亂的HTML文件或是一些無效的信息双肤。

(2)UDP協(xié)議

  • UDP協(xié)議是無連接通信協(xié)議,不保證數(shù)據(jù)的可靠傳輸钮惠,但能夠像若干個(gè)目標(biāo)發(fā)送數(shù)據(jù)茅糜,或接收來自若干個(gè)源的數(shù)據(jù)。UDP以獨(dú)立發(fā)送數(shù)據(jù)包的方式進(jìn)行素挽。這種方式就像郵遞員送信給收信人蔑赘,可以寄出很多封信給同一個(gè)人,且每一封信都是相對(duì)獨(dú)立的预明,各封信送達(dá)的順序并不重要缩赛,收信人接收信件的順序也不能保證與寄出信件的順序相同。


    UDP協(xié)議
  • UDP協(xié)議適合一些對(duì)數(shù)據(jù)準(zhǔn)確性要求不高但對(duì)傳輸速度和時(shí)效性要求非常高的網(wǎng)站撰糠,如網(wǎng)絡(luò)聊天室酥馍、在線影片和直播等。這是由于TCP協(xié)議在認(rèn)證上存在額外耗費(fèi)阅酪,可能使傳輸速度減慢旨袒,而UDP協(xié)議即使有一小部分?jǐn)?shù)據(jù)包遺失或傳送順序有所不同汁针,也不會(huì)嚴(yán)重危害該項(xiàng)通信。


    兩種協(xié)議對(duì)比

端口和套接字

端口

  • 一般而言峦失,一臺(tái)計(jì)算機(jī)只有單一的連到網(wǎng)絡(luò)的物理連接扇丛,所有的數(shù)據(jù)都通過此連接對(duì)內(nèi)、對(duì)外送達(dá)特定的計(jì)算機(jī)尉辑,這就是端口帆精。網(wǎng)絡(luò)程序設(shè)計(jì)中的端口并非真實(shí)的物理存在,而是一個(gè)假想的連接裝置隧魄。端口被規(guī)定為一個(gè)在0~65535之間的整數(shù)卓练。
  • 通常,0~1023之間的端口數(shù)用于一些知名的網(wǎng)絡(luò)服務(wù)和應(yīng)用购啄,用戶的普通網(wǎng)絡(luò)應(yīng)用程序應(yīng)該使用1024及以上的端口數(shù)襟企,以避免端口號(hào)與另外一個(gè)應(yīng)用或服務(wù)系統(tǒng)所用端口沖突。


    計(jì)算機(jī)中的端口

套接字

  • 網(wǎng)絡(luò)程序中的套接字(Socket)用于將應(yīng)用程序與端口連接起來狮含。套接字是一個(gè)假想的連接裝置顽悼,就像插座一樣可連接電器與電線,我們只需創(chuàng)建Socket類對(duì)象几迄,即可使用套接字蔚龙。


2. TCP程序設(shè)計(jì)基礎(chǔ)

  • TCP網(wǎng)絡(luò)程序需要服務(wù)器和客戶端,而且必須先有服務(wù)器映胁,服務(wù)器先啟動(dòng)木羹,等待客戶端的接入,客戶端申請(qǐng)連接解孙,待服務(wù)器響應(yīng)后則完成了連接坑填。


  • 服務(wù)器和客戶端是單獨(dú)分開的,需要建立客戶端套接字和服務(wù)器套接字弛姜。


    客戶端套接字
    服務(wù)器套接字
  • TCP程序設(shè)計(jì)需要遵循一定的流程脐瑰,首先創(chuàng)建服務(wù)器套接字,然后創(chuàng)建客戶端套接字廷臼,建立完成后服務(wù)器開啟監(jiān)聽苍在,服務(wù)器和客戶端可以同時(shí)處理數(shù)據(jù),必須要先啟動(dòng)服務(wù)器中剩,然后啟動(dòng)客戶端忌穿。


    TCP程序設(shè)計(jì)流程

下面看一個(gè)簡(jiǎn)單的TCP網(wǎng)絡(luò)程序

  • 首先創(chuàng)建一個(gè)服務(wù)器,然后創(chuàng)建一個(gè)客戶端结啼,服務(wù)器等待客戶端的接入掠剑。當(dāng)客戶端接入服務(wù)器后,客戶端和服務(wù)器通過輸入/輸出流互相給對(duì)方發(fā)消息:

服務(wù)器源代碼:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server
{
    public static void main(String[] args)
    {
        try {
            ServerSocket server = new ServerSocket(1100);
            System.out.println("服務(wù)器啟動(dòng)成功郊愧,等待用戶接入......");
            Socket client=server.accept();
            System.out.println("有客戶端接入朴译,客戶端IP:"+client.getInetAddress());

            InputStream in=client.getInputStream();
            byte[] bt=new byte[1024];
            int len=in.read(bt);
            String data=new String(bt,0,len);
            System.out.println("客戶端發(fā)來消息:"+data);

            OutputStream out=client.getOutputStream();
            String message="收到井佑,這里是服務(wù)器。";
            out.write(message.getBytes());

            client.close();
        }catch (IOException e){
        e.printStackTrace();
        }
    }
}

客戶端源代碼:


package lianxi1;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {
    public static void main(String[] args) {
        try {
            Socket client = new Socket("127.0.0.1", 1100);
            System.out.println("連接成功");

            OutputStream out=client.getOutputStream();
            String message="服務(wù)器你好眠寿,我是客戶端躬翁。";
            out.write(message.getBytes());

            InputStream in=client.getInputStream();
            byte[] bt=new byte[1024];
            int len=in.read(bt);
            String data=new String(bt,0,len);
            System.out.println("服務(wù)器發(fā)來消息:"+data);

            client.close();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

運(yùn)行結(jié)果:
沒有客戶端接入
有客戶端接入,接收到客戶端發(fā)來的消息
客戶端接入后盯拱,收到服務(wù)器發(fā)來的消息

3. UDP程序設(shè)計(jì)基礎(chǔ)

  • 用戶數(shù)據(jù)報(bào)協(xié)議(UDP)是網(wǎng)絡(luò)信息傳輸?shù)牧硪环N形式盒发。基于UDP的通信和基于TCP的通信不同狡逢,基于UDP的信息傳遞更快宁舰,但不提供可靠的保證。使用UDP傳遞數(shù)據(jù)時(shí)奢浑,用戶無法知道數(shù)據(jù)能否正確地到達(dá)主機(jī)蛮艰,也不能確定到達(dá)目的地的順序是否和發(fā)送的順序相同。雖然UDP是一種不可靠的協(xié)議雀彼,但如果需要較快地傳輸信息壤蚜,并能容忍小的錯(cuò)誤,可以考慮使用UDP徊哑。
  • 使用UDP協(xié)議通信需要數(shù)據(jù)包類(DatagramPacket)和數(shù)據(jù)包套接字(DatagramSocket)袜刷。


    數(shù)據(jù)包
    數(shù)據(jù)包套接字
  • 根據(jù)前面的知識(shí),下面創(chuàng)建一個(gè)廣播數(shù)據(jù)報(bào)程序实柠。廣播數(shù)據(jù)報(bào)是一項(xiàng)較新的技術(shù)水泉,其原理類似于電臺(tái)廣播善涨。

先編寫發(fā)消息端的程序窒盐,也就是廣播數(shù)據(jù)報(bào)程序:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Sender extends Thread
{
    int port=9898;
    InetAddress group;
    MulticastSocket socket;
    public Sender()
    {
        try {
            group=InetAddress.getByName("224.255.10.0");
            try {
                socket=new MulticastSocket(port);
                socket.joinGroup(group);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }
    public void run()
    {
        while(true) {
            DatagramPacket packet;
            Date date = new Date();
            SimpleDateFormat sf = new SimpleDateFormat("HH:mm:ss");
            String massege = "[" + sf.format(date) + "]天氣預(yù)報(bào):當(dāng)前天氣,有雨钢拧。";
            byte data[] = massege.getBytes();
            packet = new DatagramPacket(data, data.length, group, port);//創(chuàng)建數(shù)據(jù)包
            System.out.println(massege);
            try {
                socket.send(packet);
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
    public static void main(String[] args)
    {
        Sender send=new Sender();
        send.start();
    }
}

運(yùn)行結(jié)果:
廣播發(fā)出消息

  • 由運(yùn)算結(jié)果可以看到蟹漓,此時(shí)廣播數(shù)據(jù)報(bào)程序已經(jīng)編寫完畢,廣播端正在播報(bào)天氣消息源内,且一秒一更新葡粒。

接下來編寫接收端的程序:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;

public class Receive extends JFrame implements Runnable, ActionListener
{
    JButton ince=new JButton("開始接收");
    JButton stop=new JButton("停止接收");
    JTextArea inceAr=new JTextArea(10,10);
    JTextArea inced=new JTextArea(10,10);
    Thread thread;
    boolean getMessage=true;
    int port=9898;
    InetAddress group;
    MulticastSocket socket;
    public Receive()
    {
        super("廣播數(shù)據(jù)報(bào)");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        inceAr.setForeground(Color.blue);
        JPanel north=new JPanel();
        north.add(ince);
        north.add(stop);
        add(north,BorderLayout.NORTH);
        JPanel center=new JPanel();
        center.setLayout(new GridLayout(1,2));
        center.add(inceAr);
        final JScrollPane scrollPane=new JScrollPane();
        center.add(scrollPane);
        scrollPane.setViewportView(inced);
        add(center,BorderLayout.CENTER);
        thread=new Thread(this);
        ince.addActionListener(this);
        stop.addActionListener(this);
        validate();//重新驗(yàn)證容器中的組件,即刷新組件
        setBounds(100,50,360,380);//設(shè)置布局
        setVisible(true);//將窗體設(shè)置為顯示狀態(tài)
        try {
            group=InetAddress.getByName("224.255.10.0");
            try {
                socket=new MulticastSocket(port);
                socket.joinGroup(group);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }


    }
    public static void main(String[] args)
    {
        Receive rec=new Receive();
        rec.setSize(460,200);
    }
    public void actionPerformed(ActionEvent e)
    {
        if(e.getSource()==ince)
        {
            ince.setBackground(Color.red);
            stop.setBackground(Color.yellow);
            if(!thread.isAlive())
            {
                thread=new Thread(this);
                getMessage=true;
            }
            thread.start();
        }
        if(e.getSource()==stop) {
            ince.setBackground(Color.yellow);
            stop.setBackground(Color.red);
            getMessage = false;
        }
    }
    public void run()
    {
        while (getMessage)
        {
            DatagramPacket packet;
            byte data[]=new byte[1024];
            packet=new DatagramPacket(data,data.length,group,port);
            try {
                socket.receive(packet);//讀取數(shù)據(jù)包
                String message=new String(packet.getData(),0,packet.getLength());
                inceAr.setText("正在接收的內(nèi)容:"+message);
                inced.append(message+"\n");
            }catch (IOException e)
            {
                e.printStackTrace();
            }

        }
    }

}

運(yùn)行結(jié)果:
接收端接收廣播消息

  • 由運(yùn)行結(jié)果可以看到膜钓,接收端接收到了廣播數(shù)據(jù)報(bào)信息嗽交。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市颂斜,隨后出現(xiàn)的幾起案子夫壁,更是在濱河造成了極大的恐慌,老刑警劉巖沃疮,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盒让,死亡現(xiàn)場(chǎng)離奇詭異梅肤,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)邑茄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門姨蝴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人肺缕,你說我怎么就攤上這事左医。” “怎么了同木?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵炒辉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我泉手,道長(zhǎng)黔寇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任斩萌,我火速辦了婚禮缝裤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘颊郎。我一直安慰自己憋飞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布姆吭。 她就那樣靜靜地躺著榛做,像睡著了一般。 火紅的嫁衣襯著肌膚如雪内狸。 梳的紋絲不亂的頭發(fā)上检眯,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音昆淡,去河邊找鬼锰瘸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛昂灵,可吹牛的內(nèi)容都是我干的避凝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼眨补,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼管削!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起撑螺,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤含思,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后实蓬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茸俭,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吊履,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了调鬓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片艇炎。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖腾窝,靈堂內(nèi)的尸體忽然破棺而出缀踪,到底是詐尸還是另有隱情,我是刑警寧澤虹脯,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布驴娃,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜筋栋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望疆柔。 院中可真熱鬧,春花似錦镶柱、人聲如沸旷档。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鞋屈。三九已至,卻和暖如春故觅,著一層夾襖步出監(jiān)牢的瞬間厂庇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工逻卖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宋列,地道東北人昭抒。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓评也,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親灭返。 傳聞我的和親對(duì)象是個(gè)殘疾皇子盗迟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

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