当前位置: 首页 > article >正文

计算机网络 —— 网络编程实操(1)(UDP)

计算机网络 —— 网络编程实操(UDP)

  • 套接字
    • 端口
      • 套接字的定义
      • 为什么需要套接字?
  • 套接字的分类
      • 1. 按照通信协议分类
      • 2. 按照地址族(Address Family)分类
      • 3. 按照通信模式分类
  • socket API
    • sockaddr结构
  • 使用接口
    • 套接字初始化
      • socket函数 ,完成套接字的创建
      • bind,完成套接字的绑定
      • htons
      • inet_addr
  • 启动服务
    • recvfrom

套接字

端口

我们知道,我们进行网络通信的时候,我们实际上是两个应用进程在进行通信,那么这个过程是如何进行的呢?每一个进程都会领到一个自己的端口号,通过端口号来区分,当数据包通过网络到达一台主机时,目标IP地址将数据包导向正确的机器,而目标端口号则帮助操作系统决定哪个正在运行的应用程序应该接收这个数据包。例如,如果你访问一个网站,你的浏览器会将HTTP请求发送到Web服务器的80端口(如果是HTTP协议),而Web服务器会监听该端口并处理传入的请求。:

端口(Port)在网络通信中是指一个抽象的概念,用来标识特定的进程或网络服务。它是传输层协议(如TCP和UDP)的一部分,用于区分同一台计算机上的不同网络应用程序

一些网络协议非常重要,所以已经占用了一些端口号,这些端口号为熟知端口号

  • 20/21 FTP (File Transfer Protocol) - 用于文件传输的服务。20用于数据传输,21用于命令控制。
  • 22 SSH (Secure Shell) - 用于安全登录远程计算机和执行命令。
  • 23 TELNET - 一种不安全的远程登录服务协议。
  • 25 SMTP (Simple Mail Transfer Protocol) - 用于发送电子邮件。
  • 53 DNS (Domain Name System) - 用于将域名解析为IP地址。
  • 67/68 DHCP (Dynamic Host Configuration Protocol) - 用于自动分配IP地址。67是服务器端口,68是客户端口。
  • 69 TFTP (Trivial File Transfer Protocol) - 简单文件传输协议,常用于网络启动。
  • 80 HTTP (Hypertext Transfer Protocol) - 用于访问网页的超文本传输协议。
  • 110 POP3 (Post Office Protocol version 3) - 用于接收电子邮件。
  • 123 NTP (Network Time Protocol) - 用于同步计算机的时钟。
  • 143 IMAP (Internet Message Access Protocol) - 另一个用于接收电子邮件的协议。
  • 161 SNMP (Simple Network Management Protocol) - 用于管理网络设备。
  • 443 HTTPS (HTTP Secure) - 安全版本的HTTP,通过SSL/TLS加密通信。
  • 3306 MySQL Database - MySQL数据库服务器使用的端口。
  • 3389 RDP (Remote Desktop Protocol) - Windows远程桌面服务。
  • 5432 PostgreSQL Database - PostgreSQL数据库服务器使用的端口。
  • 8080 HTTP Alternate - 有时用作HTTP的备用端口,尤其是在主HTTP端口被占用或受限的情况下。

上述端口是互联网工程任务组(IETF)和其他标准化组织推荐的标准端口。不过,实际应用中可能会根据需要进行调整。例如,在企业环境中,出于安全考虑,可能会更改默认端口以避免直接攻击。此外,一些私有或内部服务也可能会选择未注册的高编号端口来运行。

剩下的端口号就是可以拿给用户使用的了,这类端口号仅仅在客户程序运行的时候才会被启用,所以又称为短暂端口号

那么套接字是什么呢?

IP地址拼接上端口号构成套接字,这样既可以知道数据包往哪里送,也可以知道最终这个数据包要交给哪一个进程。

套接字(Socket)是计算机网络编程中的一个抽象概念,它提供了一种机制,允许不同计算机上的程序通过网络进行通信。套接字可以被认为是操作系统为应用程序提供的一个接口,用于实现进程间通信(IPC),特别是跨越网络的通信。

套接字的定义

套接字是一种通信端点,它包含了一个IP地址和一个端口号,这两个信息共同标识了网络上的唯一服务。在实际操作中,套接字是一组API函数,这些函数让程序员能够创建、配置和管理网络连接,并且发送和接收数据。套接字可以支持多种协议,如TCP、UDP等。

为什么需要套接字?

  1. 跨平台一致性:套接字提供了一组标准的API,使得编写网络程序时可以在不同的操作系统上保持代码的一致性。这意味着使用套接字编写的程序可以在Linux、Windows、macOS等多个平台上运行,而不需要对网络层代码做大量的修改。
  1. 灵活性:套接字提供了对底层网络协议的直接访问,这使得开发者可以选择最适合其应用需求的协议(例如TCP对于可靠传输,UDP对于低延迟传输)。此外,还可以根据需要选择是否建立连接(面向连接或无连接的服务)。
  1. 进程间通信:除了网络通信外,套接字还可以用于同一台机器上的进程间通信。这种情况下,通常使用UNIX域套接字,它们比传统的TCP/UDP套接字更高效,因为它们不需要通过网络堆栈。
  1. 资源管理:套接字帮助管理有限的网络资源。例如,服务器可以监听特定的端口,以确保只有正确的客户端可以连接。同时,套接字还提供了关闭不活跃连接的能力,从而释放系统资源。
  1. 安全性:通过设置套接字选项,可以增强通信的安全性。例如,可以配置SSL/TLS加密来保护敏感数据的传输。
  1. 并发处理:套接字可以与多线程或多进程模型结合使用,以实现高并发处理能力。比如,Web服务器可以同时处理来自多个客户端的请求。

总之,套接字是网络编程的核心组成部分,它简化了开发人员构建网络应用程序的过程,同时也保证了不同设备之间的互操作性和兼容性。

套接字的分类

套接字有许多分类:

套接字(Socket)的类型主要由其通信协议和通信方式决定。以下是几种常见的套接字类型:

1. 按照通信协议分类

  • 流式套接字 (SOCK_STREAM)
  • 使用TCP协议,提供面向连接、可靠的数据传输服务。
  • 数据按顺序无差错地传输,确保数据的完整性和顺序性。>
  • 适用于需要保证数据传输可靠性的情况,如HTTP、HTTPS、FTP等。
  • 数据报套接字 (SOCK_DGRAM)
  • 使用UDP协议,提供无连接、不可靠的数据报服务。
  • 数据以独立的数据包形式发送,不保证到达顺序或是否丢失。
  • 适用于对实时性要求较高而对可靠性要求相对较低的应用,如视频会议、在线游戏等。
  • 原始套接字 (SOCK_RAW)
  • 允许直接访问底层网络协议,如IP、ICMP等。
  • 开发者可以构建自己的协议头,通常用于实现特殊的网络工具或诊断程序,如ping命令。
  • 需要特殊权限才能创建,并且不是所有操作系统都支持。

2. 按照地址族(Address Family)分类

  • 因特网套接字 (AF_INET 或 PF_INET)
  • 使用IPv4地址格式,是Internet上最常用的套接字类型。
  • 因特网6套接字 (AF_INET6 或 PF_INET6)
  • 使用IPv6地址格式,随着IPv6的逐渐普及而变得越来越重要。
  • UNIX域套接字 (AF_UNIX 或 PF_UNIX)
  • 也称为本地套接字,只在同一台机器上的进程间通信中使用。
  • 因为不需要通过网络层,所以比因特网套接字更快更高效。
  • 支持流式套接字和数据报套接字两种模式。

3. 按照通信模式分类

  • 面向连接的套接字
  • 在两个端点之间建立连接后才开始数据交换,类似于电话通话前先拨号建立连接。
  • 流式套接字通常是面向连接的。
  • 无连接的套接字
  • 不需要事先建立连接就可以发送数据,更像是邮寄信件,发送方不知道接收方是否存在。
  • 数据报套接字是无连接的。

选择合适的套接字类型取决于应用程序的需求,包括但不限于性能、可靠性、安全性等因素。在实际开发过程中,开发者会根据具体应用场景来决定使用哪种类型的套接字。

socket API

在Linux中进行网络编程时,主要使用的是套接字(socket)API。以下是与相关的常用接口函数:

  1. socket() - 创建一个套接字。

    int socket(int domain, int type, int protocol);
    
  2. bind() - 将地址信息(IP地址和端口号)绑定到套接字。

    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    
  3. sendto() - 向指定的地址发送数据(适用于无连接的UDP)。

    ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                   const struct sockaddr *dest_addr, socklen_t addrlen);
    
  4. recvfrom() - 接收来自任何发送方的数据,并可获取发送方的地址。

    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                     struct sockaddr *src_addr, socklen_t *addrlen);
    
  5. connect() - 对于UDP来说,可以将套接字与特定的对等体地址关联起来,这样后续就可以使用send()recv()了。

    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    
  6. send() - 如果UDP套接字已经通过connect()绑定了对等体地址,则可以直接使用此函数发送数据。

    ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    
  7. recv() - 如果UDP套接字已经通过connect()绑定了对等体地址,则可以直接使用此函数接收数据。

    ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    
  8. close() - 关闭套接字。

    int close(int sockfd);
    
  9. getsockname() - 获取套接字自身的名称(即本地地址)。

    int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    
  10. getpeername() - 获取对等体的地址(对于UDP来说,只有在调用connect()之后才有意义)。

    int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    
  11. setsockopt()getsockopt() - 分别设置和获取套接字选项。

    int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
    int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
    

这些接口提供了基本的UDP通信功能,包括创建套接字、绑定地址、发送和接收数据报以及关闭连接。在实际编程中,还需要处理错误检查和其他可能需要的功能,例如非阻塞I/O、多播等。

sockaddr结构

socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要讲的UNIX DomainSocket. 然而, 各种网络协议的地址格式并不相同

在这里插入图片描述

其中,我们的IPV4和IPV6使用struct sockaddr_in,unix使用struct sockaddr_un,在使用这些接口时,都要强转为struct sockaddr类型,这里要注意。

我们今天来简单使用一下这些接口,首先我们先准备一些工作,把对应的类写好:

使用接口

我们先把一些基本的类写好,我们这次写一个UDP的服务端:

#pragma once
#include<iostream>
#include<cstdint>

const static std::string defaultip = "0.0.0.0";
const static uint16_t defaultport = 8888;

class UdpSever
{
    public:
    //构造函数
    UdpSever(const std::string& ip ,const uint16_t port = defaultport)
        :_ip(ip),_port(port)
    {
        //如果初始化成功后,打印一下信息
        std::cout << "Success create"<< std::endl;
        std::cout << "ip address is " << _ip << std::endl;
        std::cout << "port number is " << _port << std::endl;
    }

    //初始化服务
    void Init()
    {

    }

    //开启服务
    void Start()
    {

    }


    //析构函数
    ~UdpSever()
    {

    }

    private:
    //套接字的定义,IP+端口号
    std::string _ip; //IP地址
    uint16_t _port; //端口号占16位,使用无符号16位整型

};

#include"UdpSever.hpp"
#include<memory>

int main()
{
    //智能指针
    std::unique_ptr<UdpSever> usvr = std::make_unique<UdpSever>("0.0.0.0");
    usvr->Init();
    usvr->Start();
    return 0; 
}

如果成功显示一下结果:
在这里插入图片描述
我们写了这么多,还没有用到网络接口,只是把准备工作做好,如果不想实现赋值拷贝,我们还可以写一个类禁止拷贝:

#pragma once

#include<iostream>

class nocopy
{
    public:
    nocpoy()
    {

    }

    nocopy(const nocopy&) = delete;
    const nocopy& operator= (const nocopy&) = delete;

    ~nocopy()
    {

    }

    private:
};

在这里插入图片描述

套接字初始化

socket函数 ,完成套接字的创建

我们先完成套接字的初始化,这个要用到函数socket函数:
在这里插入图片描述这个函数接受三个参数:地址族(address family)、套接字类型(socket type),以及协议(protocol),

第一个参数是地址族,手册里面告诉我们了有哪些地址族:
在这里插入图片描述我们一般使用的是IPV4,所以第一个参数设置为AF_INET。

第二个是套接字的类型,这个嘛,就是面向流还是面向报文,手册里面也解释的很清楚了:

在这里插入图片描述UDP是面向数据报的,所以我们使用SOCK_DGRAM,

第三个参数为 protocol ,一般情况下默认为0,但是如果出现多个协议使用同一种套接字,这个值就不再是0,但是我们这里不会有这种情况,所以默认为0:
在这里插入图片描述
我们看一下返回值:
在这里插入图片描述如果成功,一个文件描述符会被返回,否则置为1,一开始的UdpSever类中还要增添一个socketfd,用来接收创建之后文件描述符的值:

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

bind,完成套接字的绑定

创建好了套接字之后,就要把IP和端口号进行绑定,我们有专门的函数来完成这个工作:bind
在这里插入图片描述这个绑定要用专门的结构体来做:struct sockaddr_in
在这里插入图片描述我们转到它的定义:
在这里插入图片描述注意一下,我们要使用sockaddr_in,一般我们要包含这几个文件:
在这里插入图片描述
sockaddr_in 结构体定义在头文件 <netinet/in.h> 中。这个结构体用于表示互联网协议(IP)族的套接字地址,通常与IPv4一起使用。

为了确保兼容性和完整性,通常还会包含其他相关的头文件,如 <sys/socket.h><arpa/inet.h>,这些头文件提供了创建和操作套接字所需的功能。以下是一个完整的头文件包含列表,当你需要使用 sockaddr_in 时可以参考:

包含完成之后我们就可以对结构体进行初始化:
在这里插入图片描述但是这里还有一个问题,我们这里的_port和_ip都是字符串和整数,不是网络的可传输的形式,所以我们要有办法序列化这些信息,我们接口有提供这些函数:

htons

在这里插入图片描述htons 是 C 语言中的一个网络字节序转换函数,全称为 “Host to Network Short”。它的作用是将主机字节序(通常是小端或大端)的16位无符号短整数转换为网络字节序(大端)。网络字节序在网络通信中使用,以确保不同字节序的计算机之间能够正确地交换数据。

在大多数现代计算机系统上,CPU 使用的是小端字节序(例如 x86 架构),而网络协议规定了所有在网络上发送的数据都必须使用大端字节序。因此,在发送数据之前,需要使用 htons 函数将本地表示的数字转换为网络字节序;接收数据后,则可能需要使用 ntohs 函数将网络字节序转换回主机字节序。

inet_addr

在这里插入图片描述inet_addr 是 C 语言中的一个函数,用于将互联网标准点分十进制表示的 IPv4 地址字符串(例如 “192.168.1.1”)转换为网络字节序的二进制形式。该函数定义在头文件 <arpa/inet.h> 中。

所以我们应该这样写:
在这里插入图片描述接下来才可以绑定:

在这里插入图片描述在这里插入图片描述

启动服务

我们完成套接字的绑定之后,就可以启动服务了,这里我们可以接受发过来的信息:

recvfrom

在这里插入图片描述recvfrom 是一个用于接收来自UDP套接字的数据报的系统调用。它也可以被用来接收TCP、UNIX域套接字等其他类型的数据,但在UDP上下文中最为常见。recvfrom 允许应用程序接收数据并获取发送方的地址信息。

函数原型如下:

#include <sys/types.h>
#include <sys/socket.h>

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);
  • sockfd:这是你要从中接收数据的套接字描述符。
  • buf:这是一个指向缓冲区的指针,接收的数据将被存储在这里。
  • len:这是缓冲区的最大长度(以字节为单位),指示可以接收的最大数据量。
  • flags:这是一组标志位,用于修改接收操作的行为。常用的标志包括 MSG_PEEK(预览消息但不将其从队列中移除)、MSG_WAITALL(等待直到接收到所有请求的数据)等。
  • src_addr:这是一个指向 struct sockaddr 类型结构体的指针,该结构体用于存储发送方的地址信息。如果不需要知道发送方地址,可以将其设置为 NULL
  • addrlen:这是一个指向 socklen_t 类型变量的指针,初始时应包含 src_addr 结构体的大小。在成功返回时,它会被更新为实际填入 src_addr 的地址长度。

recvfrom 返回接收到的字节数,如果值为0,可能表示连接已经关闭(对于面向连接的协议如TCP)。如果发生错误,则返回 -1 并设置 errno 以指示错误类型。

我们启动服务之后,只要我们不干涉,服务就会一直运行,所以我们可以用一个死循环:

   //开启服务
    void Start()
    {
        char buffer[1024]; //缓冲区
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);


       ssize_t n = recvfrom(_socketfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);

       for(;;)
       {
         buffer[n] = 0;
         std::cout << "Client say#" << buffer << std::endl;
       }
    }

在这里插入图片描述
我们可以用netstat -aup来查看我们端口号的情况:

在这里插入图片描述


http://www.kler.cn/a/467829.html

相关文章:

  • 鸿蒙NEXT使用request模块实现本地文件上传
  • pytest测试用例管理框架特点及常见语法和用法分享
  • wordpress右侧浮动咨询台插件
  • 简述Linux的信号处理
  • RK3588+麒麟国产系统+FPGA+AI在电力和轨道交通视觉与采集系统的应用
  • vite6+vue3+ts+prettier+eslint9配置前端项目(后台管理系统、移动端H5项目通用配置)
  • C#利用Attribute实现面向切面编程(AOP)
  • LangChain4j 框架探索
  • 前端-计算机网络篇
  • 【Unity功能集】TextureShop纹理工坊(八)修剪工具
  • 基于Spring Boot的前后端分离的外卖点餐系统
  • 前端异常处理合集
  • python pandas 对mysql 一些常见操作
  • Vulnhub靶场(Earth)
  • 【机器学习篇】解密算法魔方之魅之机器学习的多维应用盛宴
  • C 实现植物大战僵尸(四)
  • 太速科技-633-4通道2Gsps 14bit AD采集PCie卡
  • Azkaban其二,具体使用以及告警设置
  • win10 npm login 登陆失败
  • ARM CCA机密计算安全模型之CCA认证
  • 大数据技术(六)—— Hbase集群安装
  • Oracle ADG备机报错ORA-00328 ORA-00334
  • 人工智能:是助力还是取代?
  • CSP知识点整理大全
  • arm64函数源码和汇编解析(objdump)
  • Java【线程与并发】