溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

socket套接字的案例分析

發(fā)布時(shí)間:2020-10-29 11:50:52 來源:億速云 閱讀:205 作者:小新 欄目:編程語(yǔ)言

這篇文章主要介紹socket套接字的案例分析,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

  • Mark:閱讀blog + 代碼實(shí)現(xiàn)耗時(shí)18分鐘
  • socket套接字的案例分析


文章重點(diǎn):
  • IP地址、端口號(hào)……

  • socket API

  • 實(shí)現(xiàn)UDP客戶端/服務(wù)器


  • 套接字是網(wǎng)絡(luò)編程中的一種通信機(jī)制,是支持TCP/IP的網(wǎng)絡(luò)通信的基本操作單元,可以看做是不同主機(jī)之間的進(jìn)程進(jìn)行雙向通信的端點(diǎn),簡(jiǎn)單的說就是通信的兩方的一種約定,用套接字中的相關(guān)函數(shù)來完成通信過程。

前面介紹過,本地的進(jìn)程間通信(IPC)有很多種方式,常見的總結(jié)以下幾點(diǎn):

 1、管道(包括無名管道和命名管道);
 2、消息隊(duì)列;
 3、信號(hào)量;
 4、共享存儲(chǔ)。
 5、……( Socket和Streams支持不同主機(jī)上的兩個(gè)進(jìn)程IPC)。
認(rèn)識(shí)網(wǎng)絡(luò)層通信過程:

socket套接字的案例分析

初識(shí)IP:

(IP就是:Internet協(xié)議IP)

socket套接字的案例分析

在通信時(shí),IP有源IP和目的IP之分,

對(duì)比寄快遞:網(wǎng)絡(luò)通信相當(dāng)于收發(fā)快遞,IP就是收件/發(fā)件人地址,僅僅知道地址還不行,還要知道派送人是誰?這就對(duì)比于網(wǎng)絡(luò)中的端口號(hào)概念,端口號(hào)標(biāo)識(shí)了一個(gè)進(jìn)程,告訴操作系統(tǒng),當(dāng)前這個(gè)數(shù)據(jù)交給哪一個(gè)程序進(jìn)行解析。


端口號(hào):

端口號(hào)(port)是傳輸層協(xié)議的內(nèi)容。

  • 端口號(hào)是一個(gè)2字節(jié)16位的整數(shù);

  • 端口號(hào)用來標(biāo)識(shí)一個(gè)進(jìn)程,告訴操作系統(tǒng),當(dāng)前這個(gè)數(shù)據(jù)交給哪一個(gè)程序進(jìn)行解析;

  • IP地址 + 端口號(hào)能標(biāo)識(shí)網(wǎng)絡(luò)上的某一臺(tái)主機(jī)的某一個(gè)進(jìn)程;

  • 一個(gè)端口號(hào)只能被一個(gè)進(jìn)程占用。

端口號(hào) & 進(jìn)程:
  • 概念

進(jìn)程有唯一的pid標(biāo)識(shí),端口號(hào)也能標(biāo)識(shí)進(jìn)程;

一個(gè)進(jìn)程可以綁定多個(gè)端口號(hào),一個(gè)端口號(hào)不能被多個(gè)進(jìn)程綁定。

  • 源端口號(hào) & 目的端口號(hào)

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

TCP:

(TCP)傳輸控制協(xié)議,面向連接。是一種提供可靠數(shù)據(jù)傳輸?shù)耐ㄓ脜f(xié)議。

  • 傳輸層協(xié)議

  • 有連接

  • 可靠傳輸

  • 面向字節(jié)流

UDP:

(UDP)用戶數(shù)據(jù)報(bào)協(xié)議,是一個(gè)面向無連接的協(xié)議。采用該協(xié)議不需要兩個(gè)應(yīng)用程序先建立連接。UDP協(xié)議不提供差錯(cuò)恢復(fù),不能提供數(shù)據(jù)重傳,因此該協(xié)議傳輸數(shù)據(jù)安全性差。

  • 傳輸層協(xié)議

  • 無連接

  • 不可靠傳輸

  • 面向數(shù)據(jù)報(bào)


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

socket套接字的案例分析

  • 如何定義網(wǎng)絡(luò)數(shù)據(jù)流的地址?

socket套接字的案例分析

其實(shí)很容易理解這個(gè)問題,就是C語(yǔ)言中比較講究的大小端問題。

  • 發(fā)送機(jī)按內(nèi)存地址從低到高順序發(fā)送;

  • 接收主機(jī)按內(nèi)存地址從低到高順序保存;

  • TCP/IP規(guī)定:網(wǎng)絡(luò)數(shù)據(jù)流應(yīng)采用大端字節(jié)序,即地地址高字節(jié)

  • 不論主機(jī)是大端機(jī)還是小端機(jī),都必須遵循TCP/IP規(guī)定;

  • 如果發(fā)送機(jī)是小端,就先將數(shù)據(jù)轉(zhuǎn)成大端再發(fā)送。

socket API:
//創(chuàng)建socket文件描述符  (TCP/UDP,客戶端+服務(wù)器)

int socket(int domain, int type, int protocol);

參數(shù)1(domain): 選擇創(chuàng)建的套接字所用的協(xié)議族;
 AF_INET : IPv4協(xié)議;
 AF_INET6: IPv6協(xié)議;
 AF_LOCAL: Unix域協(xié)議;
 AF_ROUTE:路由套接口;
 AF_KEY :密鑰套接口。
參數(shù)2(type):指定套接口類型,所選類型有:
 SOCK_STREAM:字節(jié)流套接字;
 SOCK_DGRAM : 數(shù)據(jù)報(bào)套接字;
 SOCK_RAW : 原始套接口。
 procotol: 使用的特定協(xié)議,一般使用默認(rèn)協(xié)議(NULL)。

//綁定端口號(hào)  (TCP/IP,服務(wù)器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);

參數(shù)1(socket) : 是由socket()調(diào)用返回的并且未作連接的套接字描述符(套接字號(hào))。
參數(shù)2(address):指向特定協(xié)議的地址指針。
參數(shù)3(address_len):上面地址結(jié)構(gòu)的長(zhǎng)度。
返回值:沒有錯(cuò)誤,bind()返回0,否則SOCKET_ERROR。

//開始監(jiān)聽socket  (TCP,服務(wù)器)
int listen(int socket, int backlog);

參數(shù)1(sockfd):是由socket()調(diào)用返回的并且未作連接的套接字描述符(套接字號(hào))。
參數(shù)2(backlog):所監(jiān)聽的端口隊(duì)列大小。

//接受請(qǐng)求  (TCP,服務(wù)器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);

參數(shù)1(socket) : 是由socket()調(diào)用返回的并且未作連接的套接字描述符(套接字號(hào))。
參數(shù)2(address):指向特定協(xié)議的地址指針。
參數(shù)3(addrlen):上面地址結(jié)構(gòu)的長(zhǎng)度。
返回值:沒有錯(cuò)誤,bind()返回0,否則SOCKET_ERROR。

//建立連接  (TCP,客戶端)
int connect(int sockfd, const struct struct sockaddr *addr, aocklen_t addrlen);
//關(guān)閉套接字
int close(int fd);

參數(shù)(fd):是由socket()調(diào)用返回的并且未作連接的套接字描述符(套接字號(hào))。

socket API是一層抽象的網(wǎng)絡(luò)編程接口,適用于各種底層網(wǎng)絡(luò)協(xié)議,如IPv4,IPv6,……

簡(jiǎn)單的TCP網(wǎng)絡(luò)程序:
  • TCP客戶—服務(wù)器程序的執(zhí)行流程圖:

socket套接字的案例分析

服務(wù)器代碼:

#include<iostream>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
using namespace std;

#define SERVER_PORT  5050               //端口號(hào)
#define SERVER_IP    "192.168.3.254"    //服務(wù)器ip
#define QUEUE_SIZE   5                  //所監(jiān)聽端口隊(duì)列大小

int main(int argc, char *argv[])
{
    //創(chuàng)建一個(gè)套接字,并檢測(cè)是否創(chuàng)建成功
    int sockSer;                        
    sockSer = socket(AF_INET, SOCK_STREAM, 0);
    if(sockSer == -1){
        perror("socket");
    }

    //設(shè)置端口可以重用,可以多個(gè)客戶端連接同一個(gè)端口,并檢測(cè)是否設(shè)置成功
    int yes = 1;
    if(setsockopt(sockSer, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){
        perror("setsockopt");
    }

    struct sockaddr_in addrSer,addrCli;        //創(chuàng)建一個(gè)記錄地址信息的結(jié)構(gòu)體
    addrSer.sin_family = AF_INET;              //所使用AF_INET協(xié)議族
    addrSer.sin_port = htons(SERVER_PORT);     //設(shè)置地址結(jié)構(gòu)體中的端口號(hào)
    addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);   //設(shè)置其中的服務(wù)器ip

    //將套接字地址與所創(chuàng)建的套接字號(hào)聯(lián)系起來。并檢測(cè)是否綁定成功
    socklen_t addrlen = sizeof(struct sockaddr);
    int res = bind(sockSer,(struct sockaddr*)&addrSer, addrlen);
    if(res == -1)
        perror("bind");

    listen(sockSer, QUEUE_SIZE);       //監(jiān)聽端口隊(duì)列是否由連接請(qǐng)求,如果有就將該端口設(shè)置位可連接狀態(tài),等待服務(wù)器接收連接

    printf("Server Wait Client Accept......\n");
    //如果監(jiān)聽到有連接請(qǐng)求接受連接請(qǐng)求。并檢測(cè)是否連接成功,成功返回0,否則返回-1
    int sockConn = accept(sockSer, (struct sockaddr*)&addrCli, &addrlen);
    if(sockConn == -1)
        perror("accept");
    else
    {
        printf("Server Accept Client OK.\n");
        printf("Client IP:> %s\n", inet_ntoa(addrCli.sin_addr));
        printf("Client Port:> %d\n",ntohs(addrCli.sin_port));
    }

    char sendbuf[256];         //申請(qǐng)一個(gè)發(fā)送緩存區(qū)
    char recvbuf[256];         //申請(qǐng)一個(gè)接收緩存區(qū)
    while(1)
    {
        printf("Ser:>");
        scanf("%s",sendbuf);
        if(strncmp(sendbuf,"quit",4) == 0)    //如果所要發(fā)送的數(shù)據(jù)為"quit",則直接退出。
            break;
        send(sockConn, sendbuf, strlen(sendbuf)+1, 0);   //發(fā)送數(shù)據(jù)
        recv(sockConn, recvbuf, 256, 0);    //接收客戶端發(fā)送的數(shù)據(jù)
        printf("Cli:> %s\n",recvbuf);
    }

    close(sockSer);         //關(guān)閉套接字
    return 0;
}

客戶端代碼:

#include<iostream>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
using namespace std;

#define SERVER_PORT  5050
#define SERVER_IP    "192.168.3.254"

int main(int argc, char *argv[])
{
    //創(chuàng)建客戶端套接字號(hào),并檢測(cè)是否創(chuàng)建成功
    int sockCli;
    sockCli = socket(AF_INET, SOCK_STREAM, 0);
    if(sockCli == -1)
        perror("socket");

    //創(chuàng)建一個(gè)地址信息結(jié)構(gòu)體,并對(duì)其內(nèi)容進(jìn)行設(shè)置
    struct sockaddr_in addrSer;     
    addrSer.sin_family = AF_INET;         //使用AF_INET協(xié)議族
    addrSer.sin_port = htons(SERVER_PORT);  //設(shè)置端口號(hào)
    addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);   //設(shè)置服務(wù)器ip

    bind(sockCli,(struct sockaddr*)&addrCli, sizeof(struct sockaddr));    //將套接字地址與所創(chuàng)建的套接字號(hào)聯(lián)系起來

    //創(chuàng)建一個(gè)與服務(wù)器的連接,并檢測(cè)連接是否成功
    socklen_t addrlen = sizeof(struct sockaddr);
    int res = connect(sockCli,(struct sockaddr*)&addrSer, addrlen);
    if(res == -1)
        perror("connect");
    else
        printf("Client Connect Server OK.\n");

    char sendbuf[256];     //申請(qǐng)一個(gè)發(fā)送數(shù)據(jù)緩存區(qū)
    char recvbuf[256];     //申請(qǐng)一個(gè)接收數(shù)據(jù)緩存區(qū)
    while(1)
    {
        recv(sockCli, recvbuf, 256, 0);    //接收來自服務(wù)器的數(shù)據(jù)
        printf("Ser:> %s\n",recvbuf);
        printf("Cli:>");
        scanf("%s",sendbuf);
        if(strncmp(sendbuf,"quit", 4) == 0)    //如果客戶端發(fā)送的數(shù)據(jù)為"quit",則退出。
            break;
        send(sockCli, sendbuf, strlen(sendbuf)+1, 0);   //發(fā)送數(shù)據(jù)
    }
    close(sockCli);       //關(guān)閉套接字
    return 0;
}
簡(jiǎn)單的UDP網(wǎng)絡(luò)程序:

socket套接字的案例分析

  • 相對(duì)與TCP來說,UDP安全性差,面向無鏈接。所以UDP地實(shí)現(xiàn)少了連接與接收連接的操作。所以在收發(fā)數(shù)據(jù)時(shí)就不能再用send()和recvfrom()了,而是用sendto()和recvto()之名從哪收發(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ù)1(sockfd):是由socket()調(diào)用返回的并且未作連接的套接字描述符(套接字號(hào))
參數(shù)2(buf):指向存有發(fā)送數(shù)據(jù)的緩沖區(qū)的指針
參數(shù)3(len):緩沖區(qū)長(zhǎng)度。
 **參數(shù)4(flags):**flags的值或?yàn)?,或?yàn)槠渌?/p>

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

參數(shù)1(sockfd):是由socket()調(diào)用返回的并且未作連接的套接字描述符(套接字號(hào))
參數(shù)2(buf):指向存有接收數(shù)據(jù)的緩沖區(qū)的指針
參數(shù)3(len):緩沖區(qū)長(zhǎng)度
 **參數(shù)4(flags):**flags的值或?yàn)?,或?yàn)槠渌?/p>

服務(wù)器端代碼:

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/socket.h>

int main()
{
    //創(chuàng)建一個(gè)套接字,并檢測(cè)是否創(chuàng)建成功
    int sockSer = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockSer == -1)
        perror("socket");

    struct sockaddr_in addrSer;  //創(chuàng)建一個(gè)記錄地址信息的結(jié)構(gòu)體 
    addrSer.sin_family = AF_INET;    //使用AF_INET協(xié)議族 
    addrSer.sin_port = htons(5050);     //設(shè)置地址結(jié)構(gòu)體中的端口號(hào)
    addrSer.sin_addr.s_addr = inet_addr("192.168.3.169");  //設(shè)置通信ip

    //將套接字地址與所創(chuàng)建的套接字號(hào)聯(lián)系起來,并檢測(cè)是否綁定成功
    socklen_t addrlen = sizeof(struct sockaddr);
    int res = bind(sockSer,(struct sockaddr*)&addrSer, addrlen);
    if(res == -1)
        perror("bind");

    char sendbuf[256];    //申請(qǐng)一個(gè)發(fā)送數(shù)據(jù)緩存區(qū)
    char recvbuf[256];    //申請(qǐng)一個(gè)接收數(shù)據(jù)緩存區(qū)
    struct sockaddr_in addrCli;
    while(1)
    {
        recvfrom(sockSer,recvbuf,256,0,(struct  sockaddr*)&addrCli, &addrlen);     //從指定地址接收客戶端數(shù)據(jù)
        printf("Cli:>%s\n",recvbuf);

        printf("Ser:>");    
        scanf("%s",sendbuf);
        sendto(sockSer,sendbuf,strlen(sendbuf)+1,0,(struct sockaddr*)&addrCli, addrlen);    //向客戶端發(fā)送數(shù)據(jù)
    }
    return 0;
}

客戶端代碼:

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/socket.h>

int main()
{
    //創(chuàng)建一個(gè)套接字,并檢測(cè)是否創(chuàng)建成功
    int sockCli = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockCli == -1){
        perror("socket");
    }

    addrSer.sin_family = AF_INET;    //使用AF_INET協(xié)議族 
    addrSer.sin_port = htons(5050);     //設(shè)置地址結(jié)構(gòu)體中的端口號(hào)
    addrSer.sin_addr.s_addr = inet_addr("192.168.3.169");  //設(shè)置通信ip
    socklen_t addrlen = sizeof(struct sockaddr);


    char sendbuf[256];    //申請(qǐng)一個(gè)發(fā)送數(shù)據(jù)緩存區(qū)
    char recvbuf[256];    //申請(qǐng)一個(gè)接收數(shù)據(jù)緩存區(qū)

    while(1){
        //向客戶端發(fā)送數(shù)據(jù)
        printf("Cli:>");
        scanf("%s",sendbuf);
        sendto(sockCli, sendbuf, strlen(sendbuf)+1, 0, (struct sockaddr*)&addrSer, addrlen);   
        接收來自客戶端的數(shù)據(jù)
        recvfrom(sockCli, recvbuf, BUFFER_SIZE, 0, (struct sockaddr*)&addrSer, &addrlen);
        printf("Ser:>%s\n", recvbuf);

    }

    return 0;
}

socket套接字的案例分析

以上是socket套接字的案例分析的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI