欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java實(shí)現(xiàn)TCP socket和UDP socket的實(shí)例

 更新時(shí)間:2022年02月11日 09:28:19   作者:清風(fēng)拂來(lái)水波不興  
這篇文章主要介紹了本文主要介紹了java實(shí)現(xiàn)TCP socket和UDP socket的實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

概述

        我們?cè)诰W(wǎng)絡(luò)編程時(shí),通常是讓我們本地的應(yīng)用程序和遠(yuǎn)程的應(yīng)用程序進(jìn)行通信,也就是分布式的進(jìn)程之間的通信,比如我寫(xiě)的程序A和小明的程序B進(jìn)行通信,我的程序運(yùn)行時(shí)在本機(jī)就是一個(gè)進(jìn)程,是有pid號(hào)的,小明的也是。那這兩個(gè)程序是怎么通信的呢?

        這就要理解網(wǎng)絡(luò)分層的概念了,網(wǎng)絡(luò)層實(shí)現(xiàn)的是主機(jī)到主機(jī)之間的通信,網(wǎng)絡(luò)層的實(shí)現(xiàn)是ip協(xié)議,通過(guò)各自的ip地址就能實(shí)現(xiàn)遠(yuǎn)程數(shù)據(jù)傳輸,而網(wǎng)絡(luò)層只是保證了主機(jī)A的數(shù)據(jù)能夠到達(dá)主機(jī)B,并不能夠識(shí)別和發(fā)送到對(duì)應(yīng)的進(jìn)程,而傳輸層實(shí)現(xiàn)的是進(jìn)程到進(jìn)程的通信,對(duì)網(wǎng)絡(luò)層的功能進(jìn)行了加強(qiáng),能夠把數(shù)據(jù)交付給對(duì)應(yīng)的進(jìn)程。

        有了傳輸層的功能,我們用戶才能進(jìn)一步的去實(shí)現(xiàn)自己的應(yīng)用層協(xié)議,實(shí)現(xiàn)應(yīng)用。當(dāng)我們需要遠(yuǎn)程通信時(shí)只需要通過(guò)下層的傳輸層傳輸數(shù)據(jù)即可。

        但是想象一下,每次我在應(yīng)用層發(fā)送數(shù)據(jù)時(shí)至少都是需要把 要發(fā)送的信息、本機(jī)ip、本地端口、目的ip和目的端口這五個(gè)數(shù)據(jù)通過(guò)層間接口交給傳輸層(對(duì)于TCP協(xié)議來(lái)說(shuō)的,UDP無(wú)連接不需要應(yīng)答所以不需要本機(jī)ip和端口),如下圖

         但是每一次應(yīng)用層向傳輸層發(fā)送數(shù)據(jù)時(shí)都需要發(fā)送這些數(shù)據(jù)是不是很多余?TCP一旦雙方建立起連接了,那么除了數(shù)據(jù)部分,其他的都是相同的,沒(méi)有必要每次發(fā)送數(shù)據(jù)都發(fā)送一遍,為了減少層間接口的傳輸量,就出現(xiàn)了socket,操作系統(tǒng)底層維護(hù)一個(gè)列表,用于存放多個(gè)socket,即多個(gè)會(huì)話關(guān)系。

        socket套接字是傳輸層提供給應(yīng)用層的一個(gè)API,底層實(shí)現(xiàn)就是一個(gè)整數(shù),是傳輸層和應(yīng)用層的一個(gè)約定,該整數(shù)就像是打開(kāi)一個(gè)文件并得到文件句柄一樣,對(duì)這個(gè)句柄進(jìn)行的操作就是對(duì)該文件進(jìn)行操作,方便管理。

有了socket之后應(yīng)用層數(shù)據(jù)的傳輸就變?yōu)榱诉@樣

傳輸層概述

        先來(lái)說(shuō)說(shuō)網(wǎng)絡(luò)層的ip,ip協(xié)議在網(wǎng)絡(luò)上發(fā)送數(shù)據(jù)包是不可靠的,有可能造成丟失,亂序等問(wèn)題,如發(fā)送數(shù)據(jù)包到對(duì)應(yīng)的路由器,路由器有接收緩沖區(qū),如果發(fā)現(xiàn)同一時(shí)刻來(lái)的數(shù)據(jù)包太多了,緩沖區(qū)放不下,它是可以把裝不下的數(shù)據(jù)包扔掉的。這就是不可靠的傳輸。

傳輸層提供了TCP和UDP兩種服務(wù):

  • TCP:對(duì)ip協(xié)議進(jìn)行了增強(qiáng),通過(guò)一些方式來(lái)達(dá)到可靠的數(shù)據(jù)傳輸
  • UDP:不可靠的數(shù)據(jù)傳輸,它只對(duì)ip增加了進(jìn)程到進(jìn)程之間的通信,其他的就沒(méi)了,原原本本

TCP套接字編程

        TCP socket反應(yīng)的是應(yīng)用進(jìn)程A和應(yīng)用進(jìn)程B會(huì)話關(guān)系的一個(gè)代表,A對(duì)對(duì)應(yīng)的socket發(fā)送數(shù)據(jù),就是A對(duì)B發(fā)送數(shù)據(jù);A對(duì)對(duì)應(yīng)的socket接收數(shù)據(jù)就是對(duì)B接收數(shù)據(jù)。

大致過(guò)程

1.服務(wù)器進(jìn)程必須運(yùn)行,創(chuàng)建一個(gè)歡迎socket,該socket和本地的端口進(jìn)行捆綁,在歡迎socket上阻塞式的等待接收客戶端的連接

2.客戶端創(chuàng)建本地的套接字,隱式捆綁到本地的端口,再指定服務(wù)器的ip和端口進(jìn)行連接。

3.服務(wù)器接受來(lái)自用戶端的請(qǐng)求 ,解除阻塞式等待,返回一個(gè) 新的socket(與歡迎socket不 一樣),與客戶端通信

4.連接API調(diào)用有效時(shí),客戶端與服務(wù)器建立了TCP連接,即可以通信了

代碼如下:

@Test
public void server() throws IOException {
    //1.建立歡迎socket,綁定一個(gè)監(jiān)聽(tīng)的端口號(hào)
    ServerSocket welcomeSocket = new ServerSocket(8080);
    //2.阻塞的等待客戶端的連接請(qǐng)求,連接請(qǐng)求到來(lái)時(shí)創(chuàng)建一個(gè)新的socket,與客戶端綁定
    Socket socket = welcomeSocket.accept();
    //6.從該socket接收數(shù)據(jù)
    InputStream is = socket.getInputStream();
    int len = 0;
    while ((len = is.read()) != -1) {
        System.out.print((char) len);
    }
    socket.close();
}
 
@Test
public void client() throws IOException {
    //3.建立一個(gè)客戶端這邊的socket
    Socket socket = new Socket();
    //4.阻塞的請(qǐng)求連接到指定ip和端口號(hào)的服務(wù)器進(jìn)程
    socket.connect(new InetSocketAddress("localhost", 8080));
    //5.發(fā)送數(shù)據(jù)到該socket
    OutputStream os = socket.getOutputStream();
    byte[] bytes = new byte[1024];
    os.write("hello".getBytes());
    socket.shutdownOutput();
}

詳細(xì)過(guò)程

首先來(lái)看java中socket的結(jié)構(gòu)體(類(lèi)),其他語(yǔ)言都大同小異。

public abstract class SocketImpl implements SocketOptions {    
    /**
     * The IP address of the remote end of this socket.遠(yuǎn)程主機(jī)的ip地址
     */
    protected InetAddress address;
    /**
     * The port number on the remote host to which this socket is connected.遠(yuǎn)程主機(jī)的端口
     */
    protected int port;
    /**
     * The local port number to which this socket is connected.本地端口
     */
    protected int localport;
    /**
     *實(shí)際還有一個(gè)本機(jī)的ip地址,被省略掉了
     */
}

InetAddress類(lèi)就不介紹了,里面就是封裝了ip地址等信息。

ServerSocket和Socket類(lèi)是一個(gè)東西,只時(shí)名字不同而已。具體可以看源碼。

可以得出來(lái)socket其實(shí)大致就是這么一個(gè)6元組(這里省略了socket的狀態(tài)),當(dāng)我們應(yīng)用進(jìn)程創(chuàng)建socket時(shí),操作系統(tǒng)給該socket一個(gè)唯一的整數(shù)標(biāo)識(shí),且肯定是要保存是哪個(gè)進(jìn)程創(chuàng)建的socket,所以pid也應(yīng)該對(duì)應(yīng)起來(lái),方便日后能給找到對(duì)應(yīng)的進(jìn)程。

 我們舉例如下圖的一個(gè)通信過(guò)程來(lái)具體的說(shuō)明socket的通信過(guò)程:

1.首先服務(wù)器進(jìn)程先建立一個(gè)歡迎socket,用于監(jiān)聽(tīng)連接請(qǐng)求,并且綁定端口號(hào)為8080,阻塞監(jiān)聽(tīng)客戶端的連接請(qǐng)求,如下圖

 2.這時(shí)候客戶端也新建一個(gè)socket(該socket以后會(huì)當(dāng)做和服務(wù)器的通信socket),這個(gè)socket不用像服務(wù)器那樣綁定一個(gè)固定的端口號(hào)用于監(jiān)聽(tīng),但是操作系統(tǒng)會(huì)給該socket綁定一個(gè)隨機(jī)的端口,這里假設(shè)是4567。如下圖,此時(shí)客戶端的1號(hào)socket還是一個(gè)無(wú)效的狀態(tài),因?yàn)檫€沒(méi)有連接

3.客戶端用剛剛建立的socket和遠(yuǎn)程服務(wù)器進(jìn)行連接connect,指明服務(wù)器的地址和對(duì)應(yīng)的端口號(hào),此時(shí)socket的狀態(tài)也就補(bǔ)齊了,隨后客戶端進(jìn)入阻塞模式,進(jìn)行TCP的三次握手,請(qǐng)求和服務(wù)器建立連接

 4.服務(wù)器和客戶端TCP連接建立好后,解除阻塞,并且返回一個(gè)新的socket(因?yàn)椴荒苷加脀elcome socket),新的socket就是服務(wù)器和客戶端的一個(gè)連接狀態(tài),該socket變?yōu)橐粋€(gè)有效狀態(tài),此時(shí)服務(wù)器繼續(xù)進(jìn)入阻塞狀態(tài)等待此socket的數(shù)據(jù)。

5. 連接建立好后,客戶端也解除阻塞,它的socket1也變?yōu)?strong>有效狀態(tài)。然后客戶端把需要發(fā)送的數(shù)據(jù)和對(duì)應(yīng)的socket1(輸出流里面封裝了socket)交給下層傳輸層,此時(shí)的傳輸層得到了它相應(yīng)的信息,根據(jù)socket就可以從表中查到需要發(fā)送的目的ip和端口,繼續(xù)交給下層直到發(fā)送到服務(wù)器。

6.服務(wù)器的tcp層收到數(shù)據(jù)包后,查看源ip、目的ip、源端口、目的端口,一一對(duì)照自己的socket表,發(fā)現(xiàn)2號(hào)socket真好對(duì)應(yīng),且得知2號(hào)socket是pid為100的應(yīng)用進(jìn)程,所以tcp把數(shù)據(jù)發(fā)送給該進(jìn)程,服務(wù)器的java進(jìn)程解除阻塞,read客戶端發(fā)送來(lái)的數(shù)據(jù)。

7.最后如果服務(wù)器和客戶端某一方?jīng)]有數(shù)據(jù)發(fā)了,不想建立連接了,就調(diào)用close方法,進(jìn)行TCP的四次揮手,解除連接,使兩邊對(duì)應(yīng)的socket都變得無(wú)效。

UDP套接字編程

        UDP是沒(méi)有建立連接這一過(guò)程的,也不需要維持會(huì)話關(guān)系,每個(gè)報(bào)文都是獨(dú)立傳輸?shù)?。因此UDP只能使用一個(gè)整數(shù)來(lái)標(biāo)志當(dāng)前應(yīng)用進(jìn)程,不能夠固定住對(duì)方的ip和端口號(hào),因?yàn)閁DP通信前不建立連接,可能現(xiàn)在發(fā)的這個(gè)ip和端口是一臺(tái)主機(jī),而下次用同樣的ip和端口發(fā)的就是另一臺(tái)主機(jī)了。所以每次發(fā)送的時(shí)候都需要指定發(fā)送的目的ip和端口。

UDP的socket大致如下

 java中UDPsocket結(jié)構(gòu)如下

public abstract class DatagramSocketImpl implements SocketOptions {
 
    /**
     * The local port number.
     */
    protected int localPort;
    /**
     * 省略本機(jī)ip
     */
}

UDP簡(jiǎn)單套接字編程如下:

@Test
public void server() throws IOException {
    //1.創(chuàng)建udp socket,并綁定到本機(jī)ip和8080端口號(hào)
    DatagramSocket socket = new DatagramSocket(8080);
    byte[] buff = new byte[100];
    //存放數(shù)據(jù)包的容器
    DatagramPacket packet = new DatagramPacket(buff, 0, buff.length);
    //接收數(shù)據(jù)包
    socket.receive(packet);
    System.out.println(new String(packet.getData(), 0, packet.getLength()));
    socket.close();
}
 
@Test
public void client() throws IOException {
    DatagramSocket socket = new DatagramSocket();
    byte[] data = "hello".getBytes();
    //指明發(fā)送端的ip和端口和數(shù)據(jù)部分
    DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("127.1.1.1"), 8080);
    //使用該socket發(fā)送數(shù)據(jù)包
    socket.send(packet);
}

詳細(xì)步驟如下:

1.服務(wù)器創(chuàng)建一個(gè)udp的socket,綁定端口8080用于監(jiān)聽(tīng)數(shù)據(jù)包,通過(guò)調(diào)用receive方法阻塞的監(jiān)聽(tīng)。

  2.客戶端創(chuàng)建一個(gè)自己的socket,該socket的端口假設(shè)操作系統(tǒng)分配的是4567

 3.客戶端發(fā)送數(shù)據(jù)包、目標(biāo)ip和目標(biāo)端口給下層的傳輸層,傳輸層就能夠得到源ip、源端口、目的ip和目的端口,然后一步一步的打包交給下層,發(fā)送到服務(wù)器主機(jī),服務(wù)器主機(jī)通過(guò)數(shù)據(jù)包的目的ip和目的端口,對(duì)比發(fā)現(xiàn)socket對(duì)應(yīng),然后把數(shù)據(jù)發(fā)送給對(duì)應(yīng)的pid號(hào)為100的應(yīng)用進(jìn)程。

4.服務(wù)器解除阻塞,收取數(shù)據(jù)。

5.最后關(guān)閉連接,刪除對(duì)應(yīng)的socket

注意的是UDP是沒(méi)有welcome socket的

補(bǔ)充

再補(bǔ)充一個(gè)小知識(shí)點(diǎn),那就是端口號(hào)和進(jìn)程的聯(lián)系

進(jìn)程pid是否可供計(jì)算機(jī)之間使用呢?

        應(yīng)用層代表的就是我們的應(yīng)用進(jìn)程,既然進(jìn)程代表著應(yīng)用層,那為什么進(jìn)程pid不能作為應(yīng)用層的標(biāo)識(shí)來(lái)進(jìn)行計(jì)算機(jī)之間傳輸呢?而是使用額外的端口號(hào)呢?

(1)首先: 單個(gè)計(jì)算機(jī)中的進(jìn)程使用pid來(lái)標(biāo)志的,但是在互聯(lián)網(wǎng)環(huán)境下使用的計(jì)算機(jī)操作系統(tǒng)種類(lèi)很多,而不同的操作系統(tǒng)又使用不同格式的進(jìn)程標(biāo)識(shí)符,為了使運(yùn)行不同操作系統(tǒng)的計(jì)算機(jī)的應(yīng)用進(jìn)程能夠互相通信,就必須使用統(tǒng)一的方法對(duì)TCP/IP體系的進(jìn)程進(jìn)行標(biāo)識(shí);

(2)其次:一個(gè)機(jī)器上運(yùn)行的進(jìn)程不能成為互聯(lián)網(wǎng)上通信的最后終點(diǎn),因?yàn)?strong>進(jìn)程的創(chuàng)建和撤銷(xiāo)都是動(dòng)態(tài)的,通信的一方幾乎無(wú)法識(shí)別對(duì)方機(jī)器的進(jìn)程是哪一個(gè);

例如:要和互聯(lián)網(wǎng)上某個(gè)郵件服務(wù)器聯(lián)系,幾乎無(wú)法得知其服務(wù)器郵件進(jìn)程的進(jìn)程標(biāo)識(shí)符,因?yàn)檫M(jìn)程標(biāo)識(shí)符是隨機(jī)分配的;所以,我們并不一定要知道這個(gè)服務(wù)器服務(wù)是由目的主機(jī)那個(gè)進(jìn)程實(shí)現(xiàn)的;

所以,不能使用進(jìn)程標(biāo)識(shí)符來(lái)做計(jì)算機(jī)之間的進(jìn)程通信標(biāo)識(shí);

如何使用端口號(hào)進(jìn)行通信?
   
    兩個(gè)計(jì)算機(jī)中進(jìn)程要互相通信,除了必須指定對(duì)方的IP地址,還需要知道對(duì)方的端口號(hào);

例如:我們寄信的過(guò)程說(shuō)明,當(dāng)我們要給某人寫(xiě)信時(shí),除了通訊地址還要有收件人的名字,這里的通訊地址就是IP地址,但是收件人的名字卻不是進(jìn)程標(biāo)識(shí)符,因?yàn)橛锌赡苓@個(gè)人用的是法文、德文、英文名字,快遞員無(wú)法識(shí)別,因此采用 “菜鳥(niǎo)驛站" 的模式,為每個(gè)地址配備多個(gè)快遞箱(端口號(hào)),快遞員只是將包裹放置具體的快遞箱(端口號(hào)),收件人通過(guò)監(jiān)聽(tīng)某個(gè)快遞箱是否有快遞(TCP或者UDP),來(lái)進(jìn)行數(shù)據(jù)接收,最終拿到需要的包裹(數(shù)據(jù));

端口號(hào)如何分配?

        (1)服務(wù)器使用的端口號(hào):

        一類(lèi)為熟知端口號(hào)或系統(tǒng)端口號(hào)(0~1023),將一些重要的應(yīng)用程序進(jìn)行登記,所以將一些端口號(hào)固定的分配給它們,以便于讓所以的用戶的了解,與之建立聯(lián)系; 

        另一類(lèi)為登記端口號(hào)(1024~49151),為那些不知名的應(yīng)用程序使用;

        (2)客戶機(jī)使用的端口號(hào):

        也稱(chēng)為短暫端口號(hào),由于這類(lèi)端口僅僅在客戶進(jìn)程進(jìn)行時(shí)才動(dòng)態(tài)選擇,留給客戶進(jìn)程短暫使用,當(dāng)通信結(jié)束后,剛才使用過(guò)的客戶端口號(hào)不復(fù)存在,可以繼續(xù)供其他客戶進(jìn)程使用;

到此這篇關(guān)于java實(shí)現(xiàn)TCP socket和UDP socket的文章就介紹到這了,更多相關(guān)java實(shí)現(xiàn)TCP socket和UDP socket內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring報(bào)錯(cuò):Error creating bean with name的問(wèn)題及解決

    Spring報(bào)錯(cuò):Error creating bean with name的問(wèn)

    這篇文章主要介紹了Spring報(bào)錯(cuò):Error creating bean with name的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • 關(guān)于junit測(cè)試需要的依賴

    關(guān)于junit測(cè)試需要的依賴

    這篇文章主要介紹了關(guān)于junit測(cè)試需要的依賴,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • C# log4net使用案例詳解

    C# log4net使用案例詳解

    這篇文章主要介紹了C# log4net使用案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • Spring Security自定義異常 AccessDeniedHandler不生效解決方法

    Spring Security自定義異常 AccessDeniedHandler不生效解決方法

    本文主要介紹了Spring Security自定義異常 AccessDeniedHandler不生效解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • Java實(shí)現(xiàn)跳躍表的示例詳解

    Java實(shí)現(xiàn)跳躍表的示例詳解

    跳表全稱(chēng)叫做跳躍表,簡(jiǎn)稱(chēng)跳表,是一個(gè)隨機(jī)化的數(shù)據(jù)結(jié)構(gòu),實(shí)質(zhì)就是一種可以進(jìn)行二分查找的有序鏈表。本文將利用Java語(yǔ)言編寫(xiě)一個(gè)跳表,需要的可以參考一下
    2022-05-05
  • Spring Security基于自定義的認(rèn)證提供器實(shí)現(xiàn)圖形驗(yàn)證碼流程解析

    Spring Security基于自定義的認(rèn)證提供器實(shí)現(xiàn)圖形驗(yàn)證碼流程解析

    這篇文章主要介紹了Spring Security基于自定義的認(rèn)證提供器實(shí)現(xiàn)圖形驗(yàn)證碼,通過(guò)本文學(xué)習(xí)下AuthenticationProvider接口的類(lèi)關(guān)系圖,感興趣的朋友跟隨小編一起看看吧
    2021-09-09
  • 一個(gè)依賴搞定?Spring?Boot?接口防盜刷的流程分析

    一個(gè)依賴搞定?Spring?Boot?接口防盜刷的流程分析

    kk-anti-reptile 是適用于基于 spring-boot 開(kāi)發(fā)的分布式系統(tǒng)的反爬蟲(chóng)組件,這篇文章主要介紹了一個(gè)依賴搞定?Spring?Boot?接口防盜刷,需要的朋友可以參考下
    2022-06-06
  • SpringMVC攔截器詳解

    SpringMVC攔截器詳解

    本篇文章主要介紹了SpringMVC攔截器配置及使用方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2021-07-07
  • 解讀nextLine().split(“[\\s]“)的意思

    解讀nextLine().split(“[\\s]“)的意思

    這篇文章主要介紹了解讀nextLine().split(“[\\s]“)的意思,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • 繼承WebMvcConfigurationSupport后自動(dòng)配置不生效及如何配置攔截器

    繼承WebMvcConfigurationSupport后自動(dòng)配置不生效及如何配置攔截器

    這篇文章主要介紹了繼承WebMvcConfigurationSupport后自動(dòng)配置不生效及如何配置攔截器,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11

最新評(píng)論