最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

概述

        我們在網(wǎng)絡(luò)編程時,通常是讓我們本地的應(yīng)用程序和遠程的應(yīng)用程序進行通信,也就是分布式的進程之間的通信,比如我寫的程序A和小明的程序B進行通信,我的程序運行時在本機就是一個進程,是有pid號的,小明的也是。那這兩個程序是怎么通信的呢?

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

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

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

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

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

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

傳輸層概述

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

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

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

TCP套接字編程

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

大致過程

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

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

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

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

代碼如下:

@Test
public void server() throws IOException {
    //1.建立歡迎socket,綁定一個監(jiān)聽的端口號
    ServerSocket welcomeSocket = new ServerSocket(8080);
    //2.阻塞的等待客戶端的連接請求,連接請求到來時創(chuàng)建一個新的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.建立一個客戶端這邊的socket
    Socket socket = new Socket();
    //4.阻塞的請求連接到指定ip和端口號的服務(wù)器進程
    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();
}

詳細過程

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

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

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

ServerSocket和Socket類是一個東西,只時名字不同而已。具體可以看源碼。

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

 我們舉例如下圖的一個通信過程來具體的說明socket的通信過程:

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

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

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

 4.服務(wù)器和客戶端TCP連接建立好后,解除阻塞,并且返回一個新的socket(因為不能占用welcome socket),新的socket就是服務(wù)器和客戶端的一個連接狀態(tài),該socket變?yōu)橐粋€有效狀態(tài),此時服務(wù)器繼續(xù)進入阻塞狀態(tài)等待此socket的數(shù)據(jù)。

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

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

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

UDP套接字編程

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

UDP的socket大致如下

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

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

UDP簡單套接字編程如下:

@Test
public void server() throws IOException {
    //1.創(chuàng)建udp socket,并綁定到本機ip和8080端口號
    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);
}

詳細步驟如下:

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

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

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

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

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

注意的是UDP是沒有welcome socket的

補充

再補充一個小知識點,那就是端口號和進程的聯(lián)系

進程pid是否可供計算機之間使用呢?

        應(yīng)用層代表的就是我們的應(yīng)用進程,既然進程代表著應(yīng)用層,那為什么進程pid不能作為應(yīng)用層的標識來進行計算機之間傳輸呢?而是使用額外的端口號呢?

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

(2)其次:一個機器上運行的進程不能成為互聯(lián)網(wǎng)上通信的最后終點,因為進程的創(chuàng)建和撤銷都是動態(tài)的,通信的一方幾乎無法識別對方機器的進程是哪一個;

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

所以,不能使用進程標識符來做計算機之間的進程通信標識;

如何使用端口號進行通信?
   
    兩個計算機中進程要互相通信,除了必須指定對方的IP地址,還需要知道對方的端口號;

例如:我們寄信的過程說明,當(dāng)我們要給某人寫信時,除了通訊地址還要有收件人的名字,這里的通訊地址就是IP地址,但是收件人的名字卻不是進程標識符,因為有可能這個人用的是法文、德文、英文名字,快遞員無法識別,因此采用 “菜鳥驛站" 的模式,為每個地址配備多個快遞箱(端口號),快遞員只是將包裹放置具體的快遞箱(端口號),收件人通過監(jiān)聽某個快遞箱是否有快遞(TCP或者UDP),來進行數(shù)據(jù)接收,最終拿到需要的包裹(數(shù)據(jù));

端口號如何分配?

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

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

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

        (2)客戶機使用的端口號:

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

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

相關(guān)文章

  • jvm垃圾回收GC調(diào)優(yōu)基礎(chǔ)原理分析

    jvm垃圾回收GC調(diào)優(yōu)基礎(chǔ)原理分析

    談到調(diào)優(yōu),這一定是針對特定場景、特定目的的事情, 對于 GC 調(diào)優(yōu)來說,首先就需要清楚調(diào)優(yōu)的目標是什么?從性能的角度看,通常關(guān)注三個方面,內(nèi)存占用(footprint)、延時(latency)和吞吐量(throughput)
    2022-01-01
  • SpringMVC通過模型視圖ModelAndView渲染視圖的實現(xiàn)

    SpringMVC通過模型視圖ModelAndView渲染視圖的實現(xiàn)

    這篇文章主要介紹了SpringMVC通過模型視圖ModelAndView渲染視圖的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • Springmvc如何實現(xiàn)向前臺傳遞數(shù)據(jù)

    Springmvc如何實現(xiàn)向前臺傳遞數(shù)據(jù)

    這篇文章主要介紹了Springmvc如何實現(xiàn)向前臺傳遞數(shù)據(jù),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-07-07
  • Javaweb實現(xiàn)郵件發(fā)送

    Javaweb實現(xiàn)郵件發(fā)送

    這篇文章主要為大家詳細介紹了Javaweb實現(xiàn)郵件發(fā)送,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • MyBatis的緩存解析

    MyBatis的緩存解析

    這篇文章主要介紹了MyBatis的緩存解析,一級緩存是SqlSession級別的,通過同一個SqlSession查詢的數(shù)據(jù)會緩存,下次查詢相同的數(shù)據(jù)就會從緩存中直接獲取,不會從數(shù)據(jù)重新訪問,前提必須是同一個SqlSession對象,并且查詢的數(shù)據(jù)相同,需要的朋友可以參考下
    2023-09-09
  • 關(guān)于遠程調(diào)用RestTemplate的使用避坑指南

    關(guān)于遠程調(diào)用RestTemplate的使用避坑指南

    這篇文章主要介紹了關(guān)于遠程調(diào)用RestTemplate的使用避坑指南,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java簡單計算圓周率完整示例

    Java簡單計算圓周率完整示例

    這篇文章主要介紹了Java簡單計算圓周率,結(jié)合完整實例形式分析了Java計算圓周率的原理與操作技巧,代碼備有較為詳盡的注釋便于理解,需要的朋友可以參考下
    2018-05-05
  • slf4j與jul、log4j1、log4j2、logback的集成原理

    slf4j與jul、log4j1、log4j2、logback的集成原理

    這篇文章主要介紹了slf4j與jul、log4j1、log4j2、logback的集成原理,以及通用日志框架與具體日志實現(xiàn)系統(tǒng)的機制機制介紹,包括依賴的jar包,jar沖突處理等
    2022-03-03
  • spring boot與spring mvc的區(qū)別及功能介紹

    spring boot與spring mvc的區(qū)別及功能介紹

    這篇文章主要介紹了spring boot與spring mvc的區(qū)別是什么以及spring boot和spring mvc功能介紹,感興趣的朋友一起看看吧
    2018-02-02
  • mybatis plus saveBatch方法方法執(zhí)行慢導(dǎo)致接口發(fā)送慢解決分析

    mybatis plus saveBatch方法方法執(zhí)行慢導(dǎo)致接口發(fā)送慢解決分析

    這篇文章主要為大家介紹了mybatis plus saveBatch方法導(dǎo)致接口發(fā)送慢解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10

最新評論

文化| 高密市| 镇赉县| 旅游| 阜阳市| 左贡县| 泽库县| 濉溪县| 涪陵区| 华阴市| 赤水市| 临沂市| 奈曼旗| 怀柔区| 漳平市| 华安县| 大名县| 棋牌| 久治县| 平南县| 门源| 普安县| 堆龙德庆县| 桑植县| 云阳县| 青浦区| 威宁| 陇南市| 兴隆县| 九龙城区| 金平| 临猗县| 楚雄市| 广昌县| 新野县| 社会| 屏东市| 黔西| 扶绥县| 巴青县| 扶风县|