소켓 소켓(TCP 및 UDP)에 대한 자세한 설명

little bottle
풀어 주다: 2023-04-05 19:38:01
앞으로
3543명이 탐색했습니다.

LInux를 배울 때 네트워크 프로그래밍 소켓은 초보자가 배우기 어려운 기초이자 어려운 점입니다. 이 기사를 통해 독자는 그림과 저자의 코드 구현을 통해 IP 주소, 포트 번호, TCP, UDP 개념 및 소켓 API 사용법을 완전히 이해할 수 있습니다. 클라이언트/서버 통신 등을 시뮬레이션합니다.

  • 마크: 블로그 + 코드 구현을 읽는 데 18분이 걸렸습니다
  • 소켓 소켓(TCP 및 UDP)에 대한 자세한 설명


글 초점:
  • IP 주소, 포트 번호...

  • 소켓 API

  • UDP 클라이언트/서버 구현


  • 소켓은 네트워크 프로그래밍의 통신 메커니즘으로 TCP/IP를 지원하는 네트워크 통신의 기본 운영 단위입니다. 간단히 말해서 엔드포인트는 통신 프로세스를 완료하기 위해 소켓의 관련 기능을 사용하는 두 통신 당사자 간의 계약입니다.

앞서 소개한 것처럼 로컬 프로세스 간 통신(IPC)에는 여러 가지 방법이 있습니다. 일반적인 방법은 다음과 같이 요약됩니다.

 1、管道(包括无名管道和命名管道);
 2、消息队列;
 3、信号量;
 4、共享存储。
 5、……( Socket和Streams支持不同主机上的两个进程IPC)。
로그인 후 복사
네트워크 계층 통신 프로세스 이해:

소켓 소켓(TCP 및 UDP)에 대한 자세한 설명

IP에 대한 첫 소개:

(IP는 인터넷 프로토콜 IP)

소켓 소켓(TCP 및 UDP)에 대한 자세한 설명

통신 중 IP는 소스 IP와 대상 IP로 구분됩니다.

고속 전송 비교: 네트워크 통신은 고속 전송 및 수신과 동일하며 IP는 수신자/발신자입니다. 주소만 알아두세요 주소는 부족하지만 배달원이 누구인지도 알아야 겠죠? 이는 네트워크의 포트 번호 개념과 비교됩니다. 포트 번호는 프로세스를 식별하고 분석을 위해 현재 데이터가 어떤 프로그램으로 전달되는지 운영 체제에 알려줍니다.


포트 번호:

포트 번호(포트)는 전송 계층 프로토콜의 내용입니다.

  • 포트 번호는 2바이트 16비트 정수입니다.

  • 포트 번호는 프로세스를 식별하고 분석을 위해 현재 데이터가 어떤 프로그램으로 전달되는지 운영 체제에 알려주는 데 사용됩니다.

    IP 주소 + 포트 번호는 네트워크에 있는 특정 호스트의 특정 프로세스를 식별할 수 있습니다.
  • 포트 번호는 하나의 프로세스만 사용할 수 있습니다.
  • 포트 번호 및 프로세스:
개념
  • 프로세스에는 고유한 pid 식별자가 있으며 포트 번호는 프로세스를 식별할 수도 있습니다.
프로세스는 여러 포트 번호에 바인딩될 수 있습니다. 포트 번호는 여러 프로세스가 바인딩되어 있을 수 없습니다.

소스 포트 번호 및 대상 포트 번호
  • 전송 계층 프로토콜(TCP/IP)의 데이터 세그먼트에는 소스 포트 번호와 대상 포트 번호라고 하는 두 개의 포트 번호가 있습니다. "데이터 누구? 누구에게? "를 설명합니다.

TCP:

(TCP) 전송 제어 프로토콜, 연결 지향. 안정적인 데이터 전송을 제공하는 일반적인 프로토콜입니다.

전송 계층 프로토콜
  • 연결됨
  • 신뢰할 수 있는 전송
  • 바이트 스트림 중심
  • UDP:
(UDP) 사용자 데이터그램 프로토콜은 연결입니다. 덜 지향적인 프로토콜. 이 프로토콜을 사용하면 두 응용 프로그램이 먼저 연결을 설정할 필요가 없습니다. UDP 프로토콜은 오류 복구 기능을 제공하지 않으며 데이터 재전송을 제공할 수 없으므로 이 프로토콜의 데이터 전송 보안이 취약합니다.

전송 계층 프로토콜
  • Connectionless
  • 신뢰할 수 없는 전송
  • 데이터그램 중심
  • 네트워크 바이트 순서:

소켓 소켓(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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:csdn.net
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿