国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一)

這篇具有很好參考價(jià)值的文章主要介紹了【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

一、網(wǎng)絡(luò)編程中的一些基礎(chǔ)知識(shí)

1、認(rèn)識(shí)端口號(hào)

在前面我們說(shuō)過(guò)可以使用IP地址來(lái)標(biāo)識(shí)一臺(tái)主機(jī),但是我們光有IP地址就可以完成通信了嘛?

答案是:不可以,當(dāng)我們的主機(jī)接收到了數(shù)據(jù)以后還要確定這個(gè)數(shù)據(jù)是發(fā)送給哪一個(gè)進(jìn)程的,兩臺(tái)主機(jī)的兩個(gè)軟件進(jìn)行網(wǎng)絡(luò)通信時(shí),我們還需要有一個(gè)其他的標(biāo)識(shí)來(lái)區(qū)分出這個(gè)數(shù)據(jù)要給哪個(gè)程序進(jìn)行解析,于是就有了端口號(hào)。

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

端口號(hào)(port)是傳輸層協(xié)議的內(nèi)容,它有以下特點(diǎn)

  • 端口號(hào)是一個(gè)2字節(jié)16位的整數(shù)。
  • 端口號(hào)用來(lái)標(biāo)識(shí)一個(gè)進(jìn)程, 告訴操作系統(tǒng)當(dāng)前的這個(gè)數(shù)據(jù)要交給哪一個(gè)進(jìn)程來(lái)處理。
  • IP地址 + 端口號(hào)能夠標(biāo)識(shí)網(wǎng)絡(luò)上的某一臺(tái)主機(jī)的某一個(gè)進(jìn)程。
  • 一個(gè)進(jìn)程可以綁定多個(gè)端口號(hào),但是一個(gè)端口號(hào)不能被多個(gè)進(jìn)程綁定

理解 “端口號(hào)” 和 “進(jìn)程ID”

我們之前在學(xué)習(xí)系統(tǒng)編程的時(shí)候, 學(xué)習(xí)了 pid 表示唯一一個(gè)進(jìn)程; 此處我們的端口號(hào)也是唯一表示一個(gè)進(jìn)程. 那么這兩者之間是怎樣的關(guān)系? 那在進(jìn)行網(wǎng)絡(luò)通信時(shí)為什么不直接用PID來(lái)代替port呢?

進(jìn)程ID(PID)是用來(lái)標(biāo)識(shí)系統(tǒng)內(nèi)所有進(jìn)程的唯一性的,它是屬于系統(tǒng)級(jí)的概念;而端口號(hào)(port)是用來(lái)標(biāo)識(shí)需要對(duì)外進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)請(qǐng)求的進(jìn)程的唯一性的,它是屬于網(wǎng)絡(luò)的概念。

一臺(tái)機(jī)器上可能會(huì)有大量的進(jìn)程,但并不是所有的進(jìn)程都要進(jìn)行網(wǎng)絡(luò)通信,可能有很大一部分的進(jìn)程是不需要進(jìn)行網(wǎng)絡(luò)通信的本地進(jìn)程,此時(shí)PID雖然也可以標(biāo)識(shí)這些網(wǎng)絡(luò)進(jìn)程的唯一性,但在該場(chǎng)景下就不太合適了,而且如果用PID代替端口號(hào),會(huì)導(dǎo)致網(wǎng)絡(luò)管理模塊與進(jìn)程管理模塊產(chǎn)生耦合關(guān)系,不利于設(shè)計(jì)出高內(nèi)聚低耦合的軟件。


所以在網(wǎng)絡(luò)通信中我們可以使用:IP地址+Port號(hào) 標(biāo)識(shí)互聯(lián)網(wǎng)中唯一的一個(gè)進(jìn)程。

此外,從上面通信的例子我們能看出網(wǎng)絡(luò)通信的本質(zhì):其實(shí)是進(jìn)程間通信!,位于不同主機(jī)中的兩個(gè)進(jìn)程通過(guò)網(wǎng)絡(luò)進(jìn)行了進(jìn)程間通信。

2、認(rèn)識(shí)TCP協(xié)議和UDP協(xié)議

傳輸層協(xié)議(TCP和UDP)的數(shù)據(jù)段中有兩個(gè)端口號(hào),分別叫做源端口號(hào)和目的端口號(hào)。 描述的是 “數(shù)據(jù)是誰(shuí)發(fā)的, 要發(fā)給誰(shuí)”。

認(rèn)識(shí)TCP協(xié)議

此處我們先對(duì)TCP(Transmission Control Protocol 傳輸控制協(xié)議)有一個(gè)直觀的認(rèn)識(shí); 后面我們?cè)僭敿?xì)討論TCP的一些細(xì)節(jié)問(wèn)題。

  • 傳輸層協(xié)議
  • 有連接,TCP協(xié)議是面向連接的,如果兩臺(tái)主機(jī)之間想要進(jìn)行數(shù)據(jù)傳輸,那么必須要先建立連接,當(dāng)連接建立成功后才能進(jìn)行數(shù)據(jù)傳輸。
  • 可靠傳輸,TCP協(xié)議是保證可靠的協(xié)議,數(shù)據(jù)在傳輸過(guò)程中如果出現(xiàn)了丟包、亂序等情況,TCP協(xié)議都有對(duì)應(yīng)的解決方法。
  • 面向字節(jié)流

認(rèn)識(shí)UDP協(xié)議

此處我們也是對(duì)UDP(User Datagram Protocol 用戶數(shù)據(jù)報(bào)協(xié)議)有一個(gè)直觀的認(rèn)識(shí),后面再詳細(xì)討論。

  • 傳輸層協(xié)議
  • 無(wú)連接,無(wú)需建立連接就可以進(jìn)行網(wǎng)絡(luò)傳輸
  • 不可靠傳輸,無(wú)連接也就意味著UDP協(xié)議是不可靠的,數(shù)據(jù)在傳輸過(guò)程中如果出現(xiàn)了丟包、亂序等情況,是沒(méi)有辦法進(jìn)行處理的。
  • 面向數(shù)據(jù)報(bào)

既然UDP協(xié)議是不可靠的,那為什么還要有UDP協(xié)議的存在?

首先,要保證數(shù)據(jù)傳輸?shù)目煽啃允切枰覀冏龈嗟墓ぷ鞯?,TCP協(xié)議雖然是一種可靠的傳輸協(xié)議,但這一定意味著TCP協(xié)議在底層需要做更多的工作,因此TCP協(xié)議底層的實(shí)現(xiàn)是比較復(fù)雜的。

同樣的,UDP協(xié)議雖然是一種不可靠的傳輸協(xié)議,但這一定意味著UDP協(xié)議在底層不需要做過(guò)多的工作,因此UDP協(xié)議底層的實(shí)現(xiàn)一定比TCP協(xié)議要簡(jiǎn)單,UDP協(xié)議雖然不可靠,但是它能夠快速的將數(shù)據(jù)發(fā)送給對(duì)方。

編寫(xiě)網(wǎng)絡(luò)通信代碼時(shí)具體采用TCP協(xié)議還是UDP協(xié)議,完全取決于上層的應(yīng)用場(chǎng)景。如果應(yīng)用場(chǎng)景嚴(yán)格要求數(shù)據(jù)在傳輸過(guò)程中的可靠性,此時(shí)我們就必須采用TCP協(xié)議,如果應(yīng)用場(chǎng)景允許數(shù)據(jù)在傳輸出現(xiàn)少量丟包,那么我們肯定優(yōu)先選擇UDP協(xié)議,因?yàn)閁DP協(xié)議足夠簡(jiǎn)單。

ps: 一些優(yōu)秀的網(wǎng)站在設(shè)計(jì)網(wǎng)絡(luò)通信算法時(shí),會(huì)同時(shí)采用TCP協(xié)議和UDP協(xié)議,當(dāng)網(wǎng)絡(luò)流暢時(shí)就使用UDP協(xié)議進(jìn)行數(shù)據(jù)傳輸,而當(dāng)網(wǎng)絡(luò)信號(hào)差時(shí)就使用TCP協(xié)議進(jìn)行數(shù)據(jù)傳輸,這樣既保證了數(shù)據(jù)的可靠性又保障了傳輸?shù)乃俾省?/p>


3、網(wǎng)絡(luò)字節(jié)序

計(jì)算機(jī)在存儲(chǔ)數(shù)據(jù)時(shí)是有大小端的概念的:

  • 大端模式: 數(shù)據(jù)的高字節(jié)內(nèi)容保存在內(nèi)存的低地址處。
  • 小端模式: 數(shù)據(jù)的高字節(jié)內(nèi)容保存在內(nèi)存的高地址處。

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

如果我們編寫(xiě)的程序只在本地機(jī)器上運(yùn)行,那么是不需要考慮大小端問(wèn)題的,因?yàn)橥慌_(tái)機(jī)器上的數(shù)據(jù)采用的存儲(chǔ)方式都是一樣的,要么采用的都是大端存儲(chǔ)模式,要么采用的都是小端存儲(chǔ)模式。但如果涉及網(wǎng)絡(luò)通信,那就必須考慮大小端的問(wèn)題,否則對(duì)端主機(jī)識(shí)別出來(lái)的數(shù)據(jù)可能與發(fā)送端想要發(fā)送的數(shù)據(jù)是不一致的,那么如何定義網(wǎng)絡(luò)數(shù)據(jù)流的地址呢?

  • TCP/IP協(xié)議規(guī)定,網(wǎng)絡(luò)數(shù)據(jù)流應(yīng)采用大端字節(jié)序,即低地址高字節(jié)。
  • 發(fā)送主機(jī)通常將發(fā)送緩沖區(qū)中的數(shù)據(jù)按內(nèi)存地址從低到高的順序發(fā)出。
  • 接收主機(jī)把從網(wǎng)絡(luò)上接到的字節(jié)依次保存在接收緩沖區(qū)中,也是按內(nèi)存地址從低到高的順序保存。
  • 因此,網(wǎng)絡(luò)數(shù)據(jù)流的地址應(yīng)這樣規(guī)定:先發(fā)出的數(shù)據(jù)是低地址,后發(fā)出的數(shù)據(jù)是高地址.
  • 不管這臺(tái)主機(jī)是大端機(jī)還是小端機(jī), 都會(huì)按照這個(gè)TCP/IP規(guī)定的網(wǎng)絡(luò)字節(jié)序來(lái)發(fā)送/接收數(shù)據(jù)。
  • 如果當(dāng)前發(fā)送主機(jī)是小端,就需要先將數(shù)據(jù)轉(zhuǎn)成大端,否則就忽略直接發(fā)送即可;

需要注意的是,所有的大小端的轉(zhuǎn)化工作是由操作系統(tǒng)來(lái)完成的,因?yàn)樵摬僮鲗儆谕ㄐ偶?xì)節(jié),不過(guò)也有部分的信息需要我們自行進(jìn)行處理,比如端口號(hào)和IP地址。


為使網(wǎng)絡(luò)程序具有可移植性,使同樣的C代碼在大端和小端計(jì)算機(jī)上編譯后都能正常運(yùn)行,可以調(diào)用以下庫(kù)函數(shù)做網(wǎng)絡(luò)字節(jié)序和主機(jī)字節(jié)序的轉(zhuǎn)換。

這些函數(shù)名很好記,h表示host,n表示network,l表示32位長(zhǎng)整數(shù),s表示16位短整數(shù)。

  • 如果主機(jī)是小端字節(jié)序,這些函數(shù)將參數(shù)做相應(yīng)的大小端轉(zhuǎn)換然后返回;
  • 如果主機(jī)是大端字節(jié)序,這些 函數(shù)不做轉(zhuǎn)換,將參數(shù)原封不動(dòng)地返回。

例如htonl表示將32位的長(zhǎng)整數(shù)從主機(jī)字節(jié)序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序,例如將IP地址轉(zhuǎn)換后準(zhǔn)備發(fā)送。

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

二、socket編程


socket 是“套接字”的意思,學(xué)習(xí) socket 編程,也就是學(xué)習(xí)計(jì)算機(jī)之間如何通信,并用編程語(yǔ)言來(lái)實(shí)現(xiàn)它。

socket API是一層抽象的網(wǎng)絡(luò)編程接口,適用于各種底層網(wǎng)絡(luò)協(xié)議,如IPv4、IPv6、UNIX Domain Socket。然而各種網(wǎng)絡(luò)協(xié)議的地址格式并不相同。

1、sockaddr結(jié)構(gòu)

套接字不僅支持跨網(wǎng)絡(luò)的進(jìn)程間通信,還支持本地的進(jìn)程間通信(域間套接字)。在進(jìn)行跨網(wǎng)絡(luò)通信時(shí)我們需要傳遞的端口號(hào)和IP地址,而本地通信則不需要,因此套接字提供了sockaddr_in結(jié)構(gòu)體和sockaddr_un結(jié)構(gòu)體,其中sockaddr_in結(jié)構(gòu)體是用于跨網(wǎng)絡(luò)通信的,而sockaddr_un結(jié)構(gòu)體是用于本地通信的。

為了讓套接字的網(wǎng)絡(luò)通信和本地通信能夠使用同一套函數(shù)接口,于是就出現(xiàn)了sockaddr結(jié)構(gòu)體,該結(jié)構(gòu)體與sockaddr_insockaddr_un的結(jié)構(gòu)都不相同,但這三個(gè)結(jié)構(gòu)體頭部的16個(gè)比特位都是一樣的,這個(gè)字段叫做協(xié)議家族。

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

此時(shí)當(dāng)我們?cè)趥鬟f在傳參時(shí),就不用傳入sockeaddr_in *sockeaddr_un *這樣的結(jié)構(gòu)體,而統(tǒng)一傳入sockeaddr *這樣的結(jié)構(gòu)體。在設(shè)置參數(shù)時(shí)就可以通過(guò)設(shè)置協(xié)議家族這個(gè)字段,來(lái)表明我們是要進(jìn)行網(wǎng)絡(luò)通信還是本地通信,在這些API內(nèi)部就可以提取sockeaddr結(jié)構(gòu)頭部的16位進(jìn)行識(shí)別,進(jìn)而得出我們是要進(jìn)行網(wǎng)絡(luò)通信還是本地通信,然后執(zhí)行對(duì)應(yīng)的操作。此時(shí)我們就通過(guò)通用sockaddr結(jié)構(gòu),將套接字網(wǎng)絡(luò)通信和本地通信的參數(shù)類型進(jìn)行了統(tǒng)一。

sockaddr結(jié)構(gòu)體

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

sockaddr_in 結(jié)構(gòu)體

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

  • IPv4和IPv6的地址格式定義在netinet/in.h中,IPv4地址用sockaddr_in結(jié)構(gòu)體表示,包括16位地址類型,16位端口號(hào)和32位IP地址。
  • IPv4、IPv6地址類型分別定義為常數(shù)AF_INET、AF_INET6。這樣,只要取得某種sockaddr結(jié)構(gòu)體的首地址,不需要知道具體是哪種類型的sockaddr結(jié)構(gòu)體,就可以根據(jù)地址類型字段確定結(jié)構(gòu)體中的內(nèi)容。
  • socket API可以都用struct sockaddr* 類型表示,在使用的時(shí)候需要強(qiáng)制轉(zhuǎn)化成sockaddr_in;這樣的好處是程序的通用性,可以接收IPv4、IPv6,以及UNIX Domain Socket各種類型的sockaddr結(jié)構(gòu)體指針做為參數(shù)。

2、簡(jiǎn)單的UDP網(wǎng)絡(luò)程序

Ⅰ、服務(wù)器的創(chuàng)建

UDP服務(wù)器的初始化就只需要創(chuàng)建套接字綁定就行了


創(chuàng)建套接字

// 創(chuàng)建 socket 文件描述符 
int socket(int domain, int type, int protocol);

功能:socket函數(shù)可以打開(kāi)一個(gè)網(wǎng)絡(luò)文件,用于網(wǎng)絡(luò)數(shù)據(jù)的通信。

對(duì)于一般的普通文件來(lái)說(shuō),當(dāng)用戶通過(guò)文件描述符將數(shù)據(jù)寫(xiě)到文件緩沖區(qū),然后再把數(shù)據(jù)刷到磁盤上就完成了數(shù)據(jù)的寫(xiě)入操作。

而對(duì)于現(xiàn)在socket函數(shù)打開(kāi)的“網(wǎng)絡(luò)文件”來(lái)說(shuō),當(dāng)用戶將數(shù)據(jù)寫(xiě)到文件緩沖區(qū)后,操作系統(tǒng)會(huì)定期將數(shù)據(jù)刷到網(wǎng)卡里面,而網(wǎng)卡則是負(fù)責(zé)數(shù)據(jù)發(fā)送的,因此數(shù)據(jù)最終就發(fā)送到了網(wǎng)絡(luò)當(dāng)中。

參數(shù)說(shuō)明:

  • domain:創(chuàng)建套接字的域(協(xié)議家族),也就是創(chuàng)建套接字的類型。該參數(shù)就相當(dāng)于struct sockaddr結(jié)構(gòu)的前16個(gè)位。如果是本地通信就設(shè)置為AF_UNIX,如果是網(wǎng)絡(luò)通信就設(shè)置為AF_INET(IPv4)或AF_INET6(IPv6)。
  • type:創(chuàng)建套接字時(shí)所需的服務(wù)類型。如果是基于UDP的網(wǎng)絡(luò)通信,我們采用的就是SOCK_DGRAM,叫做用戶數(shù)據(jù)報(bào)服務(wù),如果是基于TCP的網(wǎng)絡(luò)通信,我們采用的就是SOCK_STREAM,叫做流式套接字,提供的是流式服務(wù)。
  • protocol:創(chuàng)建套接字的協(xié)議類別。你可以指明為TCP或UDP,但該字段一般直接設(shè)置為0就可以了,設(shè)置為0表示的就是默認(rèn),此時(shí)會(huì)根據(jù)傳入的前兩個(gè)參數(shù)自動(dòng)推導(dǎo)出你最終需要使用的是哪種協(xié)議。

返回值說(shuō)明:

  • 套接字創(chuàng)建成功返回一個(gè)文件描述符,創(chuàng)建失敗返回-1,同時(shí)錯(cuò)誤碼會(huì)被設(shè)置。

示例代碼:

// udp_server.hpp

#include <iostream>
#include <cstring>
#include <cerrno>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

class UdpServer
{
public:
    UdpServer()
    {}
    
    void UdpServerInit()
    {
        // 1. 創(chuàng)建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            std::cerr << "create socket fail : " << strerror(errno) << std::endl;
            exit(1);
        }
        std::cout << "create socket success! " << "sockfd : " << _sockfd << std::endl;
    }
    
    
    ~UdpServer()
    {
        if (_sockfd > 0)
        {
            close(_sockfd);
        }
    }
    
private:
    int _sockfd;            // 套接字的文件描述符
};
// udp_server.cpp

#include "udp_server.hpp"
#include <iostream>
#include <memory>

int main()
{
    std::unique_ptr<UdpServer> up(new UdpServer());
    up->UdpServerInit();
    return 0;
}

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言


綁定函數(shù)

將程序的端口號(hào),IP地址等數(shù)據(jù)設(shè)置進(jìn)入操作系統(tǒng)內(nèi)核中

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

參數(shù)說(shuō)明:

  • sockfd:要綁定的文件的文件描述符。也就是我們創(chuàng)建套接字時(shí)獲取到的文件描述符。
  • addr:網(wǎng)絡(luò)相關(guān)的屬性信息,包括協(xié)議家族、IP地址、端口號(hào)等。
  • addrlen:傳入的addr結(jié)構(gòu)體的長(zhǎng)度。

返回值說(shuō)明:

  • 綁定成功返回0,綁定失敗返回-1,同時(shí)錯(cuò)誤碼會(huì)被設(shè)置。

將點(diǎn)分10進(jìn)制的ip轉(zhuǎn)換為整數(shù)

in_addr_t inet_addr(const char *cp);

功能:該函數(shù)可以將主機(jī)序列的字符串風(fēng)格類型的IP, 轉(zhuǎn)換成為網(wǎng)絡(luò)序列中的整數(shù)風(fēng)格的IP地址。

將整數(shù)轉(zhuǎn)換為點(diǎn)分10進(jìn)制的ip

char *inet_ntoa(struct in_addr in);

功能: 該函數(shù)可以將網(wǎng)絡(luò)序列中的整數(shù)風(fēng)格的IP地址,轉(zhuǎn)換成為主機(jī)序列的字符串風(fēng)格類型的數(shù)據(jù)。

ps : 這兩個(gè)函數(shù)調(diào)用完畢以后不需要再進(jìn)行網(wǎng)絡(luò)序列與主機(jī)序列的轉(zhuǎn)化了。


套接字創(chuàng)建完畢后我們就需要進(jìn)行綁定了,但在綁定之前我們需要先定義一個(gè)struct sockaddr_in結(jié)構(gòu),將對(duì)應(yīng)的網(wǎng)絡(luò)屬性信息填充到該結(jié)構(gòu)當(dāng)中,然后通過(guò)bind函數(shù)設(shè)置進(jìn)入操作系統(tǒng)內(nèi)核當(dāng)中,由于該結(jié)構(gòu)體當(dāng)中還有部分選填字段,因此我們最好在填充之前對(duì)該結(jié)構(gòu)體變量里面的內(nèi)容進(jìn)行清空,然后再將協(xié)議家族、端口號(hào)、IP地址等信息填充到該結(jié)構(gòu)體變量當(dāng)中。

需要注意的是,在發(fā)送到網(wǎng)絡(luò)之前需要將端口號(hào)和IP轉(zhuǎn)換為網(wǎng)絡(luò)序列,由于端口號(hào)是16位的,因此我們需要使用前面說(shuō)到的htons函數(shù)將端口號(hào)轉(zhuǎn)為網(wǎng)絡(luò)序列。此外,由于網(wǎng)絡(luò)當(dāng)中傳輸?shù)氖钦麛?shù)IP,我們需要調(diào)用inet_addr函數(shù)將字符串IP轉(zhuǎn)換成整數(shù)IP。

當(dāng)網(wǎng)絡(luò)屬性信息填充完畢后,由于bind函數(shù)提供的是通用參數(shù)類型,因此在傳入結(jié)構(gòu)體地址時(shí)還需要將struct sockaddr_in*強(qiáng)轉(zhuǎn)為struct sockaddr*類型后再進(jìn)行傳入。

// udp_server.hpp

#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <cerrno>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

enum { SOCKET_ERR = 1, BIND_ERR};

// 默認(rèn)端口號(hào)
const static uint16_t default_port = 8080;

class UdpServer
{
public:
    UdpServer(std::string ip, uint16_t port = default_port)
        :_port(port), _ip(ip)
    {
        std::cout << "ip : " << _ip << " port : " << _port << std::endl;
    }

    void UdpServerInit()
    {
        // 1. 創(chuàng)建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            std::cerr << "create socket fail : " << strerror(errno) << std::endl;
            exit(SOCKET_ERR);
        }
        std::cout << "create socket success! " << "sockfd : " << _sockfd << std::endl;

        // 2. 填充sockaddr_in結(jié)構(gòu)體
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        
        local.sin_family = AF_INET;
        // 將主機(jī)序列轉(zhuǎn)換為網(wǎng)絡(luò)序列
        local.sin_addr.s_addr = inet_addr(_ip.c_str());
        local.sin_port = htons(_port);

        // 3. 綁定IP,端口號(hào)
        if (bind(_sockfd, (struct sockaddr*)&local, sizeof(local)) != 0)
        {
            std::cerr << "bind fail :" << strerror(errno) << std::endl;
            exit(BIND_ERR);
        }
        std::cout << "bind success :" << std::endl;
    }
    
    ~UdpServer()
    {
        if (_sockfd > 0)
        {
            close(_sockfd);
        }
    }
    
private:
    int _sockfd;            // 套接字的文件描述符
    std::string _ip;        // ip地址
    uint16_t _port;         // 端口號(hào)
};
// udp_server.cpp

#include "udp_server.hpp"
#include <iostream>
#include <memory>

int main()
{
    std::unique_ptr<UdpServer> up(new UdpServer("1.1.1.1", 8080));
    up->UdpServerInit();
    return 0;
}

運(yùn)行結(jié)果,可以看出bind失敗了,這與云服務(wù)器有關(guān),云服務(wù)器不允許我們隨意綁定ip,需要讓服務(wù)器自己指定IP地址。

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

當(dāng)然,云服務(wù)器不允許我們隨意綁定ip,也有一定的道理,因?yàn)閷?duì)于一款服務(wù)器來(lái)說(shuō),這臺(tái)設(shè)備可能有多個(gè)網(wǎng)卡,這臺(tái)設(shè)備可能有多個(gè)IP,如果我們只綁定某個(gè)特定的IP就會(huì)導(dǎo)致只有某個(gè)IP能夠收到數(shù)據(jù),當(dāng)數(shù)據(jù)量很大的時(shí)候,傳輸?shù)男什⒉皇呛芨?,所以我們可以設(shè)置IP為INADDR_ANY,設(shè)置這個(gè)IP表示:綁定本主機(jī)上面的所有IP。

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

INADDR_ANY的值本質(zhì)就是0,不存在大小端的問(wèn)題,因此在設(shè)置時(shí)可以不進(jìn)行網(wǎng)絡(luò)字節(jié)序的轉(zhuǎn)換。

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

Ⅱ、運(yùn)行服務(wù)器

當(dāng)服務(wù)器初始化完畢后我們就可以啟動(dòng)服務(wù)器了,由于服務(wù)器是一個(gè)永不退出的進(jìn)程,所以服務(wù)器運(yùn)行以后一定是一個(gè)死循環(huán)!

讀取數(shù)據(jù)

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
					 struct sockaddr *src_addr, socklen_t *addrlen);

功能:

  • 從網(wǎng)絡(luò)中讀取數(shù)據(jù)。

參數(shù)說(shuō)明:

  • sockfd:創(chuàng)建的套接字對(duì)應(yīng)的文件描述符,表示從該文件描述符索引的文件當(dāng)中讀取數(shù)據(jù)。
  • buf:讀取到的數(shù)據(jù)的存放位置。
  • len:期望讀取數(shù)據(jù)的字節(jié)數(shù)。
  • flags:讀取的方式。一般設(shè)置為0,表示阻塞讀取。
  • src_addr:對(duì)端網(wǎng)絡(luò)相關(guān)的屬性信息,包括協(xié)議家族、IP地址、端口號(hào)等。
  • addrlensrc_addr結(jié)構(gòu)體的長(zhǎng)度,返回時(shí)此值會(huì)被修改為實(shí)際讀取到的src_addr結(jié)構(gòu)體的長(zhǎng)度,這是一個(gè)輸入輸出型參數(shù)。

返回值說(shuō)明:

  • 讀取成功返回實(shí)際讀取到的字節(jié)數(shù),讀取失敗返回-1,同時(shí)錯(cuò)誤碼會(huì)被設(shè)置。

發(fā)送數(shù)據(jù)

 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);

功能:

  • 將數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)中。

參數(shù)說(shuō)明:

  • sockfd:創(chuàng)建的套接字對(duì)應(yīng)的文件描述符,表示將數(shù)據(jù)寫(xiě)入該文件描述符索引的文件當(dāng)中。
  • buf:待寫(xiě)入數(shù)據(jù)的起始地址。
  • len:期望寫(xiě)入數(shù)據(jù)的字節(jié)數(shù)。
  • flags:寫(xiě)入的方式,一般設(shè)置為0,表示阻塞寫(xiě)入。
  • dest_addr:對(duì)端網(wǎng)絡(luò)相關(guān)的屬性信息,包括協(xié)議家族、IP地址、端口號(hào)等。
  • addrlen:傳入dest_addr結(jié)構(gòu)體的長(zhǎng)度。

返回值說(shuō)明:

  • 寫(xiě)入成功返回實(shí)際寫(xiě)入的字節(jié)數(shù),寫(xiě)入失敗返回-1,同時(shí)錯(cuò)誤碼會(huì)被設(shè)置。

現(xiàn)在服務(wù)端通過(guò)recvfrom函數(shù)讀取客戶端數(shù)據(jù),我們可以先將讀取到的數(shù)據(jù)當(dāng)作字符串看待,將讀取到的數(shù)據(jù)的最后一個(gè)位置設(shè)置為’\0’,此時(shí)我們就可以將讀取到的數(shù)據(jù)進(jìn)行輸出,同時(shí)我們也可以將獲取到的客戶端的IP地址和端口號(hào)也一并進(jìn)行輸出。

需要注意的是,我們獲取到的客戶端的端口號(hào)此時(shí)是網(wǎng)絡(luò)序列,我們需要調(diào)用ntohs函數(shù)將其轉(zhuǎn)為主機(jī)序列再進(jìn)行打印輸出。同時(shí),我們獲取到的客戶端的IP地址是整數(shù)IP,我們需要通過(guò)調(diào)用inet_ntoa函數(shù)將其轉(zhuǎn)為字符串IP再進(jìn)行打印輸出。

// udp_server.hpp

#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <cerrno>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

enum
{
    SOCKET_ERR = 1,
    BIND_ERR,
    USAGE_ERR
};

class UdpServer
{
public:
    UdpServer(uint16_t port)
        :_port(port)
    {
        std::cout << "port : " << _port << std::endl;
    }

    void UdpServerInit()
    {
        // 1. 創(chuàng)建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            std::cerr << "create socket fail : " << strerror(errno) << std::endl;
            exit(SOCKET_ERR);
        }
        std::cout << "create socket success! " << "sockfd : " << _sockfd << std::endl;

        // 2. 填充sockaddr_in結(jié)構(gòu)體
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        
        local.sin_family = AF_INET;
        // 將主機(jī)序列轉(zhuǎn)換為網(wǎng)絡(luò)序列
        local.sin_addr.s_addr = INADDR_ANY;
        local.sin_port = htons(_port);

        // 3. 綁定IP,端口號(hào)
        if (bind(_sockfd, (struct sockaddr*)&local, sizeof(local)) != 0)
        {
            std::cerr << "bind fail :" << strerror(errno) << std::endl;
            exit(BIND_ERR);
        }
        std::cout << "bind success !" << std::endl;
    }
    
    void UdpServerStart()
    {
    	// 緩沖區(qū)
        char buf[2048];
        // 網(wǎng)絡(luò)信息結(jié)構(gòu)體
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        
        // 死循環(huán)不能讓服務(wù)器退出
        while (true)
        {
            memset(&peer, 0, len);

            // 收取消息
            ssize_t num = recvfrom(_sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&peer, &len);
            if (num < 0)
            {
                std::cerr << "recvfrom fail !" << std::endl;
                continue;
            }
            else
            {
            	// 結(jié)尾補(bǔ)上\0,形成C風(fēng)格字符串
                buf[num] = '\0';
            }

            // 提取客戶端的ip和端口號(hào)
            std::string peer_ip = inet_ntoa(peer.sin_addr);
            uint16_t peer_port = ntohs(peer.sin_port);

            std::cout << peer_ip << " | " << peer_port << " |# " << buf << std::endl;

            // 發(fā)消息
            sendto(_sockfd, buf, strlen(buf), 0, (struct sockaddr*)&peer, len);
        }
    }
    
    ~UdpServer()
    {
        if (_sockfd > 0)
        {
            close(_sockfd);
        }
    }
    
private:
    int _sockfd;            // 套接字的文件描述符
    uint16_t _port;         // 端口號(hào)
};

我們服務(wù)器啟動(dòng)的時(shí)候需要指定端口號(hào),所以這里使用了命令行參數(shù)。

// udp_server.cpp

#include "udp_server.hpp"
#include <iostream>
#include <memory>

// 使用手冊(cè)
static void usage(std::string proc)
{
    std::cout << "usage\n\t" << proc << " 端口號(hào)" << std::endl;
}

// 命令行參數(shù),必須輸入兩個(gè)參數(shù),一個(gè)是程序名,一個(gè)是端口號(hào)
int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        usage(argv[0]);
        exit(USAGE_ERR);
    }
    
    // 提取本地端口號(hào)
    uint16_t port = atoi(argv[1]);
    std::unique_ptr<UdpServer> up(new UdpServer(port));
    up->UdpServerInit();
    up->UdpServerStart();
    return 0;
}

程序啟動(dòng)以后我們可以使用netstat -naup顯示進(jìn)程的網(wǎng)絡(luò)信息。

netstat常用選項(xiàng)說(shuō)明:

  • -n:直接使用IP地址,而不通過(guò)域名服務(wù)器。
  • -a: 顯示所有連接中的接口信息。
  • -t:顯示TCP傳輸協(xié)議的連線狀況。
  • -u:顯示UDP傳輸協(xié)議的連線狀況。
  • -p:顯示正在使用Socket的程序識(shí)別碼和程序名稱。

運(yùn)行結(jié)果

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

查看網(wǎng)絡(luò)信息

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

netstat命令顯示的信息中:

  • Proto表示協(xié)議的類型
  • Recv-Q表示網(wǎng)絡(luò)接收隊(duì)列
  • Send-Q表示網(wǎng)絡(luò)發(fā)送隊(duì)列
  • Local Address表示本地地址,
  • Foreign Address表示外部地址
  • State表示當(dāng)前的狀態(tài)
  • PID表示該進(jìn)程的進(jìn)程ID
  • Program name表示該進(jìn)程的程序名稱。

其中Foreign Address寫(xiě)成0.0.0.0:*表示任意IP地址、任意的端口號(hào)的程序都可以訪問(wèn)當(dāng)前進(jìn)程。

Ⅲ、關(guān)于客戶端的綁定問(wèn)題

首先,由于是網(wǎng)絡(luò)通信,通信雙方都需要找到對(duì)方,因此服務(wù)端和客戶端都需要有各自的IP地址和端口號(hào),只不過(guò)服務(wù)端需要顯示的進(jìn)行IP和端口號(hào)的綁定,而客戶端不需要顯示的進(jìn)行綁定的,這個(gè)綁定的工作由操作系統(tǒng)來(lái)進(jìn)行綁定,當(dāng)我們調(diào)用類似于sendto這樣的接口時(shí),操作系統(tǒng)會(huì)自動(dòng)給當(dāng)前客戶端獲取一個(gè)唯一的端口號(hào)。

服務(wù)器是為了給客戶提供服務(wù)的,因此服務(wù)器必須要讓客戶知道自己的IP地址和端口號(hào),否則客戶端是無(wú)法向服務(wù)端發(fā)起請(qǐng)求的,這就是服務(wù)端要進(jìn)行顯示綁定的原因,只有一個(gè)進(jìn)程綁定了端口號(hào)之后這個(gè)端口號(hào)才真正屬于自己,因?yàn)橐粋€(gè)端口只能被一個(gè)進(jìn)程所綁定,服務(wù)器綁定一個(gè)端口就是為了獨(dú)占這個(gè)端口。

而客戶端在通信時(shí)雖然也需要端口號(hào),但客戶端一般是不進(jìn)行綁定的,客戶端訪問(wèn)服務(wù)端的時(shí)候,端口號(hào)只要是唯一的就行了,不需要明確是那個(gè)特定的端口號(hào)。

一臺(tái)設(shè)備上可以運(yùn)行很多客戶端,例如:B站客戶端綁定了8080端口號(hào),那么以后8080端口號(hào)就只能給B站客戶端使用,如果8080端口號(hào)又被淘寶客戶端綁定了并且淘寶先啟動(dòng)了,那么B站客戶端就無(wú)法啟動(dòng)了,因此客戶端端口通常是不綁定,由OS動(dòng)態(tài)分配,也就是說(shuō),客戶端每次啟動(dòng)時(shí)使用的端口號(hào)可能是變化的,此時(shí)只要我們的端口號(hào)沒(méi)有被耗盡,客戶端就永遠(yuǎn)可以啟動(dòng)。

Ⅳ、啟動(dòng)客戶端

客戶端的編寫(xiě)與服務(wù)端類似,只不過(guò)客戶端不需要我們進(jìn)行綁定工作的,此外作為一個(gè)客戶端,它必須知道它要訪問(wèn)的服務(wù)端的IP地址和端口號(hào),因此在我們啟動(dòng)客戶端時(shí)中需要引入服務(wù)端的IP地址和端口號(hào)。

客戶端和服務(wù)端在功能上是相互補(bǔ)充的,我們上面的服務(wù)器是在讀取客戶端發(fā)來(lái)的數(shù)據(jù)然后回發(fā)回去,那么這里我們的客戶端就應(yīng)該向服務(wù)端發(fā)送數(shù)據(jù),然后接收服務(wù)器回發(fā)的數(shù)據(jù)。

// client.cpp

#include <iostream>
#include <cstring>
#include <cerrno>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

enum
{
    SOCKET_ERR = 1,
    BIND_ERR,
    USAGE_ERR
};

// 使用手冊(cè)
static void usage(std::string proc)
{
    std::cout << "usage\n\t" << proc << " IP 端口" << std::endl;
}
// 命令行參數(shù),必須輸入三個(gè)參數(shù),一個(gè)是程序名,一個(gè)是IP,一個(gè)是端口號(hào)
int main(int argc, char* argv[])
{

    if (argc != 3)
    {
        usage(argv[0]);
        exit(USAGE_ERR);
    }

    // 1. 得到服務(wù)器的IP和端口
    std::string server_ip = argv[1];
    uint16_t server_port = atoi(argv[2]);

    // 2. 創(chuàng)建套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        std::cerr << "create socket fail : " << strerror(errno) << std::endl;
        exit(SOCKET_ERR);
    }

    // 3. 填充server結(jié)構(gòu)體
    struct sockaddr_in server;
    socklen_t len = sizeof(server);
    memset(&server, 0, len);

    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(server_ip.c_str());
    server.sin_port = htons(server_port);


    // 4. 業(yè)務(wù)處理
    std::string message;
    char buf[2048];
    while (true)
    {
        std::cout << "[pan的服務(wù)器] :> ";
        getline(std::cin, message);
        // 發(fā)送消息
        // 在我們首次調(diào)用系統(tǒng)調(diào)用發(fā)送數(shù)據(jù)時(shí),OS會(huì)隨機(jī)選擇一個(gè)端口號(hào) + 自己的IP進(jìn)行bind
        ssize_t num = sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, len);
        if (num < 0)
        {
            std::cerr << "sendto fail !" << std::endl;
            continue;
        }

        struct sockaddr_in temp;
        socklen_t temp_len = sizeof(temp);
        memset(&temp, 0, temp_len);
        // 收消息
        num = recvfrom(sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&temp, &temp_len);
        if (num < 0)
        {
            std::cerr << "recvfrom fail !" << std::endl;
            continue;
        }
        else
        {
            buf[num] = '\0';
        }
        std::cout << "server's message | " << buf << std::endl;
    }
    return 0;
}

Ⅴ、本地測(cè)試

現(xiàn)在服務(wù)端和客戶端的代碼都已經(jīng)編寫(xiě)完畢,我們可以先進(jìn)行本地測(cè)試,現(xiàn)在我們運(yùn)行服務(wù)器時(shí)指明端口號(hào)為8080,再運(yùn)行客戶端,此時(shí)客戶端要訪問(wèn)的服務(wù)器的IP地址就是本地環(huán)回127.0.0.1地址,服務(wù)端的端口號(hào)就是8080。

  • 127.0.0.1 :本地環(huán)回,表示當(dāng)前主機(jī)的地址,通常用來(lái)進(jìn)行本地通信或測(cè)試。

我們要讓服務(wù)端先運(yùn)行,然后再讓客戶端運(yùn)行,之后提示我們進(jìn)行輸入,當(dāng)我們?cè)诳蛻舳溯斎霐?shù)據(jù)后,客戶端將數(shù)據(jù)發(fā)送給服務(wù)端,此時(shí)服務(wù)端再將收到的數(shù)據(jù)打印輸出后回發(fā),這時(shí)我們?cè)诜?wù)端和客戶端的窗口都能看到我們輸入的內(nèi)容。
【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

此時(shí)我們?cè)儆?code>netstat命令查看網(wǎng)絡(luò)信息,可以看到服務(wù)端的端口是8080,客戶端的端口是44777。這里客戶端能被netstat命令查看到,說(shuō)明客戶端也已經(jīng)動(dòng)態(tài)綁定成功了,這就是我們所謂的網(wǎng)絡(luò)通信。

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

Ⅵ、網(wǎng)絡(luò)測(cè)試

如果你是云服務(wù)器,請(qǐng)確保你想使用的端口已經(jīng)開(kāi)放,下面是騰訊云的云服務(wù)器開(kāi)放端口的方法:

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

好了,我們開(kāi)始進(jìn)行網(wǎng)絡(luò)測(cè)試:

【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一),網(wǎng)絡(luò),網(wǎng)絡(luò),開(kāi)發(fā)語(yǔ)言

你可以將此客戶端軟件給更多的人,讓它們都能夠連接你的服務(wù)器,進(jìn)行網(wǎng)絡(luò)通信。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-735045.html

到了這里,關(guān)于【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 網(wǎng)絡(luò)編程2(套接字編程)

    網(wǎng)絡(luò)編程2(套接字編程)

    套接字編程:如何編寫(xiě)一個(gè)網(wǎng)絡(luò)通信程序 1.網(wǎng)絡(luò)通信的數(shù)據(jù)中都會(huì)包含一個(gè)完整的五元組: sip,sport,dip,dport,protocol(源IP,源端口,對(duì)端IP,對(duì)端端口,協(xié)議) 五元組完整的描述了數(shù)據(jù)從哪來(lái),到哪去,用什么數(shù)據(jù)格式 2.網(wǎng)絡(luò)通信–兩個(gè)主機(jī)進(jìn)程之間的通信:客戶端服務(wù)

    2023年04月09日
    瀏覽(116)
  • 【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一)

    【網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(一)

    在前面我們說(shuō)過(guò)可以使用IP地址來(lái)標(biāo)識(shí)一臺(tái)主機(jī),但是我們光有IP地址就可以完成通信了嘛? 答案是:不可以,當(dāng)我們的主機(jī)接收到了數(shù)據(jù)以后還要確定這個(gè)數(shù)據(jù)是發(fā)送給哪一個(gè)進(jìn)程的,兩臺(tái)主機(jī)的兩個(gè)軟件進(jìn)行網(wǎng)絡(luò)通信時(shí),我們還需要有一個(gè)其他的標(biāo)識(shí)來(lái)區(qū)分出這個(gè)數(shù)據(jù)要給

    2024年02月06日
    瀏覽(96)
  • 【JaveEE】網(wǎng)絡(luò)編程之TCP套接字、UDP套接字

    【JaveEE】網(wǎng)絡(luò)編程之TCP套接字、UDP套接字

    目錄 1.網(wǎng)絡(luò)編程的基本概念 1.1為什么需要網(wǎng)絡(luò)編程? 1.2服務(wù)端與用戶端 1.3網(wǎng)絡(luò)編程五元組? 1.4套接字的概念 2.UDP套接字編程 2.1UDP套接字的特點(diǎn) ?2.2UDP套接字API 2.2.1DatagramSocket類 2.2.2DatagramPacket類? 2.2.3基于UDP的回顯程序 2.2.4基于UDP的單詞查詢? 3.TCP套接字編程 3.1TCP套接字的特

    2023年04月13日
    瀏覽(915)
  • 【JavaEE】網(wǎng)絡(luò)編程之TCP套接字、UDP套接字

    【JavaEE】網(wǎng)絡(luò)編程之TCP套接字、UDP套接字

    目錄 1.網(wǎng)絡(luò)編程的基本概念 1.1為什么需要網(wǎng)絡(luò)編程? 1.2服務(wù)端與用戶端 1.3網(wǎng)絡(luò)編程五元組? 1.4套接字的概念 2.UDP套接字編程 2.1UDP套接字的特點(diǎn) ?2.2UDP套接字API 2.2.1DatagramSocket類 2.2.2DatagramPacket類? 2.2.3基于UDP的回顯程序 2.2.4基于UDP的單詞查詢? 3.TCP套接字編程 3.1TCP套接字的特

    2023年04月20日
    瀏覽(120)
  • 【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字二

    【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字二

    喜歡的點(diǎn)贊,收藏,關(guān)注一下把! TCP和UDP在編程接口上是非常像的,前面我們說(shuō)過(guò)TCP是面向連接的,UDP我們上篇博客也寫(xiě)過(guò)了,我們發(fā)現(xiàn)UDP服務(wù)端客戶端寫(xiě)好啟動(dòng)直接就發(fā)消息了沒(méi)有建立連接。TCP是建立連接的,注定在寫(xiě)的時(shí)候肯定有寫(xiě)不一樣的地方。具體怎么不一樣,我們

    2024年04月15日
    瀏覽(101)
  • 網(wǎng)絡(luò)編程套接字(3)——Java數(shù)據(jù)報(bào)套接字(UDP協(xié)議)

    網(wǎng)絡(luò)編程套接字(3)——Java數(shù)據(jù)報(bào)套接字(UDP協(xié)議)

    目錄 一、Java數(shù)據(jù)報(bào)套接字通信模型 二、UDP數(shù)據(jù)報(bào)套接字編程 1、DatagramSocket ????????(1)DatagramSocket構(gòu)造方法 ????????(2)DatagramSocket方法 2、DatagramPacket ????????(1)DatagramPacket構(gòu)造方法 ????????(2)DatagramPacket方法 3、InetSocketAddress 三、代碼示例:回顯服務(wù)

    2024年03月12日
    瀏覽(233)
  • 網(wǎng)絡(luò)編程【TCP流套接字編程】

    網(wǎng)絡(luò)編程【TCP流套接字編程】

    目錄 TCP流套接字編程 1.ServerSocket API 2.Socket API 3.TCP中的長(zhǎng)短連接 4.回顯程序(短連接) 5.服務(wù)器和客戶端它們的交互過(guò)程 6.運(yùn)行結(jié)果及修改代碼 ? ??兩個(gè)核心: ServerSocket? ? ?Socket 1.ServerSocket API ? ServerSocket 是創(chuàng)建?TCP服務(wù)端Socket的API ServerSocket 構(gòu)造方法: ServerSocket 方法 :

    2023年04月12日
    瀏覽(573)
  • UDP網(wǎng)絡(luò)套接字編程

    UDP網(wǎng)絡(luò)套接字編程

    先來(lái)說(shuō)說(shuō)數(shù)據(jù)在網(wǎng)絡(luò)上的傳輸過(guò)程吧,我們知道系統(tǒng)其實(shí)終究是根據(jù)馮諾依曼來(lái)構(gòu)成的,而網(wǎng)絡(luò)數(shù)據(jù)是怎么發(fā)的呢? 其實(shí)很簡(jiǎn)單,網(wǎng)絡(luò)有五層。如下: 如上圖,我們知道的是,每層對(duì)應(yīng)的操作系統(tǒng)中的那些地方,有些可能說(shuō)是網(wǎng)絡(luò)有七層,其實(shí)和這個(gè)五層一樣的。下面我們

    2024年02月04日
    瀏覽(97)
  • 【網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(三)TCP網(wǎng)絡(luò)程序

    【網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字(三)TCP網(wǎng)絡(luò)程序

    與前邊的UDP網(wǎng)絡(luò)程序相同,創(chuàng)建套接字的接口都是socket,下邊對(duì)socket接口進(jìn)行介紹: 協(xié)議家族選擇AF_INET,因?yàn)槲覀円M(jìn)行網(wǎng)絡(luò)通信。 而第二個(gè)參數(shù),為服務(wù)類型,傳入SOCK_STREAM,我們編寫(xiě)TCP程序,所以要選擇流式的服務(wù)。 第三個(gè)參數(shù)默認(rèn)傳入0,由前兩個(gè)參數(shù)就可以推出這是

    2024年02月16日
    瀏覽(92)
  • 【網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程 和 Socket 套接字認(rèn)識(shí)

    【網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程 和 Socket 套接字認(rèn)識(shí)

    ?個(gè)人主頁(yè):bit me?? ?當(dāng)前專欄:Java EE初階?? 用戶在瀏覽器中,打開(kāi)在線視頻網(wǎng)站,如優(yōu)酷看視頻,實(shí)質(zhì)是通過(guò)網(wǎng)絡(luò),獲取到網(wǎng)絡(luò)上的一個(gè)視頻資源。 與本地打開(kāi)視頻文件類似,只是視頻文件這個(gè)資源的來(lái)源是網(wǎng)絡(luò)。 相比本地資源來(lái)說(shuō),網(wǎng)絡(luò)提供了更為豐富的網(wǎng)絡(luò)資源:

    2023年04月15日
    瀏覽(516)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包