ホームページ  >  記事  >  バックエンド開発  >  ソケットソケット(TCP、UDP)の詳しい説明

ソケットソケット(TCP、UDP)の詳しい説明

little bottle
little bottle転載
2019-04-10 15:24:123515ブラウズ

LInux を学習する場合、ネットワーク プログラミング ソケットは基礎であり、初心者にとっては学習の難しい点です。この記事を通じて、読者は図と著者のコードを通じて IP アドレス、ポート番号、TCP、UDP の概念、ソケット API を完全に理解できます。実装のアイデア、使用法、クライアント/サーバー通信のシミュレートなど

  • マーク: ブログのコード実装を読むのに 18 分かかりました<br>
  • ソケットソケット(TCP、UDP)の詳しい説明


article 重要なポイント:
  • #IP アドレス、ポート番号...

  • ソケット API

  • UDP クライアントの実装 クライアント/サーバー


    ソケットは、ネットワーク プログラミングにおける通信メカニズムであり、TCP/IP をサポートするネットワーク通信の基本操作単位です。これは、異なるホスト上のプロセス間の双方向通信のためのエンドポイントです。簡単に言うと、通信する 2 つの当事者間の合意です。ソケット内の関連する関数は、通信プロセスを完了するために使用されます。
前に紹介したように、ローカル プロセス間通信 (IPC) にはさまざまな方法があります。一般的な方法は次の点にまとめられています。

 1、管道(包括无名管道和命名管道);
 2、消息队列;
 3、信号量;
 4、共享存储。
 5、……( Socket和Streams支持不同主机上的两个进程IPC)。
ネットワーク層の通信プロセスを理解する:

ソケットソケット(TCP、UDP)の詳しい説明

IP との最初の知り合い:
(IP は: インターネット プロトコル IP)

ソケットソケット(TCP、UDP)の詳しい説明

通信中、IP は送信元 IP と宛先 IP に分けられます

速達の送信の比較: ネットワーク通信は速達の送信と受信に相当します IP は受信者/送信者のアドレスです。アドレスがわかれば十分です。だけでは不十分です。送信者が誰であるかを知る必要もあります。誰ですか?これはネットワークのポート番号の概念と比較され、ポート番号はプロセスを識別し、分析のために現在のデータがどのプログラムに渡されるかをオペレーティング システムに伝えます。


ポート番号:
ポート番号 (ポート) は、トランスポート層プロトコルの内容です。

  • ポート番号は 2 バイトの 16 ビット整数です。

  • ポート番号は、プロセスを識別し、動作しているプロセスを通知するために使用されます。現在のどのプログラムにデータを分析のために引き渡す必要があるか、

  • #IP アドレスとポート番号により、ネットワーク上の特定のホストの特定のプロセスを識別できます。
  • # #ポート番号は 1 つのプロセスによってのみ占有できます。

  • ポート番号とプロセス:

コンセプト
  • プロセスには独自のpid 識別、ポート番号でプロセスを識別することもできます。
1 つのプロセスを複数のポート番号にバインドできますが、1 つのポート番号を複数のプロセスにバインドすることはできません。

送信元ポート番号と宛先ポート番号
  • トランスポート層プロトコル (TCP/IP) のデータ セグメント送信元ポート番号と宛先ポート番号と呼ばれる 2 つのポート番号は、「データが誰のものか? 誰に送信されるか?」を表します。
TCP:

(TCP ) 送信制御プロトコル、接続指向。これは、信頼性の高いデータ送信を提供する一般的なプロトコルです。
#トランスポート層プロトコル

  • 接続済み

  • 信頼性の高い伝送

  • #バイト ストリーム指向
  • UDP:
  • (UDP) ユーザー データグラム プロトコルは、コネクションレス指向のプロトコルです。このプロトコルを使用する場合、2 つのアプリケーションが最初に接続を確立する必要はありません。 UDP プロトコルにはエラー回復機能がなく、データの再送信もできないため、このプロトコルのデータ送信セキュリティは不十分です。

#トランスポート層プロトコル

接続なし
  • 信頼性の低い伝送
  • データグラム指向
  • ネットワークバイトオーダー:

# 定義方法ネットワークデータフローのアドレス?

ソケットソケット(TCP、UDP)の詳しい説明

  • 実は、この問題は、C 言語のビッグ エンディアンとスモール エンディアンの問題であり、非常に理解しやすいです。
送信機はメモリ アドレスの低位から高位の順に送信し、

ソケットソケット(TCP、UDP)の詳しい説明

#受信ホストはメモリ アドレスの低位から高位の順に保存します;

  • TCP/IP 規制: ネットワーク データ フローではビッグ エンディアン バイト オーダー、つまり

    アドレス上位バイト
  • ;
  • ホストがビッグ エンディアン マシンであってもリトル エンディアン マシンであっても、TCP/IP 規則に従う必要があります;
  • 送信側がリトル エンディアンの場合は、データを送信する前にビッグエンディアンに変換します。

socket API:
//创建socket文件描述符  (TCP/UDP,客户端+服务器)

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

参数1(domain): 选择创建的套接字所用的协议族; <br>  AF_INET : IPv4协议; <br>  AF_INET6: IPv6协议; <br>  AF_LOCAL: Unix域协议; <br>  AF_ROUTE:路由套接口; <br>  AF_KEY :密钥套接口。 <br>参数2(type):指定套接口类型,所选类型有: <br>  SOCK_STREAM:字节流套接字; <br>  SOCK_DGRAM : 数据报套接字; <br>  SOCK_RAW : 原始套接口。 <br>  procotol: 使用的特定协议,一般使用默认协议(NULL)。

//绑定端口号  (TCP/IP,服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);

参数1(socket) : 是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。 <br>参数2(address):指向特定协议的地址指针。 <br>参数3(address_len):上面地址结构的长度。 <br>返回值:没有错误,bind()返回0,否则SOCKET_ERROR。

//开始监听socket  (TCP,服务器)
int listen(int socket, int backlog);

参数1(sockfd):是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。 <br>参数2(backlog):所监听的端口队列大小。

//接受请求  (TCP,服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);

参数1(socket) : 是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。 <br>参数2(address):指向特定协议的地址指针。 <br>参数3(addrlen):上面地址结构的长度。 <br>返回值:没有错误,bind()返回0,否则SOCKET_ERROR。

//建立连接  (TCP,客户端)
int connect(int sockfd, const struct struct sockaddr *addr, aocklen_t addrlen);
//关闭套接字
int close(int fd);

参数(fd):是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。

socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4,IPv6,……

简单的TCP网络程序:
  • TCP客户—服务器程序的执行流程图:

ソケットソケット(TCP、UDP)の詳しい説明

服务器代码:

#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"    //服务器ip
#define QUEUE_SIZE   5                  //所监听端口队列大小

int main(int argc, char *argv[])
{
    //创建一个套接字,并检测是否创建成功
    int sockSer;                        
    sockSer = socket(AF_INET, SOCK_STREAM, 0);
    if(sockSer == -1){
        perror("socket");
    }

    //设置端口可以重用,可以多个客户端连接同一个端口,并检测是否设置成功
    int yes = 1;
    if(setsockopt(sockSer, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){
        perror("setsockopt");
    }

    struct sockaddr_in addrSer,addrCli;        //创建一个记录地址信息的结构体
    addrSer.sin_family = AF_INET;              //所使用AF_INET协议族
    addrSer.sin_port = htons(SERVER_PORT);     //设置地址结构体中的端口号
    addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);   //设置其中的服务器ip

    //将套接字地址与所创建的套接字号联系起来。并检测是否绑定成功
    socklen_t addrlen = sizeof(struct sockaddr);
    int res = bind(sockSer,(struct sockaddr*)&addrSer, addrlen);
    if(res == -1)
        perror("bind");

    listen(sockSer, QUEUE_SIZE);       //监听端口队列是否由连接请求,如果有就将该端口设置位可连接状态,等待服务器接收连接

    printf("Server Wait Client Accept......\n");
    //如果监听到有连接请求接受连接请求。并检测是否连接成功,成功返回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];         //申请一个发送缓存区
    char recvbuf[256];         //申请一个接收缓存区
    while(1)
    {
        printf("Ser:>");
        scanf("%s",sendbuf);
        if(strncmp(sendbuf,"quit",4) == 0)    //如果所要发送的数据为"quit",则直接退出。
            break;
        send(sockConn, sendbuf, strlen(sendbuf)+1, 0);   //发送数据
        recv(sockConn, recvbuf, 256, 0);    //接收客户端发送的数据
        printf("Cli:> %s\n",recvbuf);
    }

    close(sockSer);         //关闭套接字
    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[])
{
    //创建客户端套接字号,并检测是否创建成功
    int sockCli;
    sockCli = socket(AF_INET, SOCK_STREAM, 0);
    if(sockCli == -1)
        perror("socket");

    //创建一个地址信息结构体,并对其内容进行设置
    struct sockaddr_in addrSer;     
    addrSer.sin_family = AF_INET;         //使用AF_INET协议族
    addrSer.sin_port = htons(SERVER_PORT);  //设置端口号
    addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);   //设置服务器ip

    bind(sockCli,(struct sockaddr*)&addrCli, sizeof(struct sockaddr));    //将套接字地址与所创建的套接字号联系起来

    //创建一个与服务器的连接,并检测连接是否成功
    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];     //申请一个发送数据缓存区
    char recvbuf[256];     //申请一个接收数据缓存区
    while(1)
    {
        recv(sockCli, recvbuf, 256, 0);    //接收来自服务器的数据
        printf("Ser:> %s\n",recvbuf);
        printf("Cli:>");
        scanf("%s",sendbuf);
        if(strncmp(sendbuf,"quit", 4) == 0)    //如果客户端发送的数据为"quit",则退出。
            break;
        send(sockCli, sendbuf, strlen(sendbuf)+1, 0);   //发送数据
    }
    close(sockCli);       //关闭套接字
    return 0;
}
简单的UDP网络程序:

ソケットソケット(TCP、UDP)の詳しい説明

  • 相对与TCP来说,UDP安全性差,面向无链接。所以UDP地实现少了连接与接收连接的操作。所以在收发数据时就不能再用send()和recvfrom()了,而是用sendto()和recvto()之名从哪收发数据。
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

参数1(sockfd):是由socket()调用返回的并且未作连接的套接字描述符(套接字号) <br>参数2(buf):指向存有发送数据的缓冲区的指针 <br>参数3(len):缓冲区长度。 <br>  **参数4(flags):**flags的值或为0,或为其他

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

参数1(sockfd):是由socket()调用返回的并且未作连接的套接字描述符(套接字号) <br>参数2(buf):指向存有接收数据的缓冲区的指针 <br>参数3(len):缓冲区长度 <br>  **参数4(flags):**flags的值或为0,或为其他

服务器端代码:

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

int main()
{
    //创建一个套接字,并检测是否创建成功
    int sockSer = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockSer == -1)
        perror("socket");

    struct sockaddr_in addrSer;  //创建一个记录地址信息的结构体 
    addrSer.sin_family = AF_INET;    //使用AF_INET协议族 
    addrSer.sin_port = htons(5050);     //设置地址结构体中的端口号
    addrSer.sin_addr.s_addr = inet_addr("192.168.3.169");  //设置通信ip

    //将套接字地址与所创建的套接字号联系起来,并检测是否绑定成功
    socklen_t addrlen = sizeof(struct sockaddr);
    int res = bind(sockSer,(struct sockaddr*)&addrSer, addrlen);
    if(res == -1)
        perror("bind");

    char sendbuf[256];    //申请一个发送数据缓存区
    char recvbuf[256];    //申请一个接收数据缓存区
    struct sockaddr_in addrCli;
    while(1)
    {
        recvfrom(sockSer,recvbuf,256,0,(struct  sockaddr*)&addrCli, &addrlen);     //从指定地址接收客户端数据
        printf("Cli:>%s\n",recvbuf);

        printf("Ser:>");    
        scanf("%s",sendbuf);
        sendto(sockSer,sendbuf,strlen(sendbuf)+1,0,(struct sockaddr*)&addrCli, addrlen);    //向客户端发送数据
    }
    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()
{
    //创建一个套接字,并检测是否创建成功
    int sockCli = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockCli == -1){
        perror("socket");
    }

    addrSer.sin_family = AF_INET;    //使用AF_INET协议族 
    addrSer.sin_port = htons(5050);     //设置地址结构体中的端口号
    addrSer.sin_addr.s_addr = inet_addr("192.168.3.169");  //设置通信ip
    socklen_t addrlen = sizeof(struct sockaddr);


    char sendbuf[256];    //申请一个发送数据缓存区
    char recvbuf[256];    //申请一个接收数据缓存区

    while(1){
        //向客户端发送数据
        printf("Cli:>");
        scanf("%s",sendbuf);
        sendto(sockCli, sendbuf, strlen(sendbuf)+1, 0, (struct sockaddr*)&addrSer, addrlen);   
        接收来自客户端的数据
        recvfrom(sockCli, recvbuf, BUFFER_SIZE, 0, (struct sockaddr*)&addrSer, &addrlen);
        printf("Ser:>%s\n", recvbuf);

    }

    return 0;
}

ソケットソケット(TCP、UDP)の詳しい説明

<br>
【推荐课程:TCP/IP视频教程

以上がソケットソケット(TCP、UDP)の詳しい説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。