网络:2.Socket编程UDP

Socket编程UDP

一.接口补充

1. socket(系统调用)

作用:创建一个套接字的一端

函数原型

cpp 复制代码
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
  • 参数
    • domain(协议族/地址族)
      • AF_INET : IPv4(常用)
      • AF_INET6: IPv6
      • AF_UNIX : 本地进程间通信(Unix 域)
    • type(套接字类型)
      • SOCK_DGRAM : 数据报(UDP)
      • SOCK_STREAM : 字节流(TCP)
    • protocol(协议编号)
      • 0就行,让系统按domain+type自动选择合适的协议.
  • 返回值
    • 成功:返回一个套接字的文件描述符
    • 失败:返回-1,并设置 errno

2. bind(系统调用)

作用:将创建好的套接字与本地的 IP 地址和端口号绑定在一起。(告诉操作系统:我这个 socket 要监听哪个本地地址和端口。)

函数原型

cpp 复制代码
#include <sys/types.h>
#include <sys/socket.h>

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

    • sockfd:套接字的文件描述符;

    • addr(地址信息结构体):

      • 用于指定要绑定的本地 IP 和端口号。

      • 对 IPv4 通常是 struct sockaddr_in 类型(需强转为 struct sockaddr*)。

      • 关键字段:

        cpp 复制代码
        struct sockaddr_in {
            sa_family_t    sin_family;   // 地址族,网络通信选AF_INET/AF_INET6,进程通信选AF_UNIX
            in_port_t      sin_port;     // 端口号(网络字节序)
            struct in_addr sin_addr;     // IP地址(网络字节序)
        };

        struct sockaddr_in需要的头文件:

        #include <sys/socket.h>

        #include <netinet/in.h>

        #include <arpa/inet.h>

    • addrlen:地址结构体的大小:sizeof(struct sockaddr_in)

  • 返回值

    • 成功: 0
    • 失败: -1,并设置 errno

3. bzero(库函数)

作用: 将指定内存区域的前 n 个字节全部清零(置为 0); 常用于初始化结构体、数组或缓冲区。

函数原型

cpp 复制代码
#include <strings.h>

void bzero(void *s, size_t n);
  • 参数
    • *s:要清零的内存起始地址(void 类型);**
    • n:要清零的字节数.

4. inet_addr(库函数)

作用: 将一个点分十进制的 IPv4 地址字符串(如 "192.168.1.1")转换为网络字节序的32 位整数(in_addr_t(uint32_t)类型)。 常用于为 sockaddr_in.sin_addr.s_addr 赋值。

函数原型

cpp 复制代码
#include <arpa/inet.h>

in_addr_t inet_addr(const char *cp);
  • 参数

    • cp
      • C 风格字符串形式的 IPv4 地址,例如 "127.0.0.1""192.168.10.5";
      • 要求是 点分十进制格式(四个 0--255 的数字,用点分隔).
  • 返回值

    • 成功: 返回对应的 网络字节序 IP 地址(in_addr_t 类型,实质为 uint32_t);
    • 失败: 返回常量 INADDR_NONE(即 0xFFFFFFFF).

5. inet_ntoa(库函数)

作用: 将一个网络字节序的 IPv4 地址(struct in_addr 类型)转换为"点分十进制字符串。

函数原型

cpp 复制代码
#include <arpa/inet.h>

char *inet_ntoa(struct in_addr in);
  • 参数:in

    • struct in_addr 类型,表示一个 IPv4 地址
    • 通常从 sockaddr_in.sin_addr 成员获得;
  • 返回值

    • 成功: 返回一个 指向静态字符串 的指针,内容为点分十进制形式的 IPv4 地址;

    • 失败: 无特别错误返回,但注意:

      该字符串存放在静态内存区中,每次调用 inet_ntoa() 都会覆盖上一次的结果,非线程安全。


6. inet_pton(库函数)

作用:将"点分十进制"或"冒号分隔"的IP地址字符串(IPv4/IPv6)转换为网络字节序的二进制格式,是inet_addr()的现代、安全版本。

函数原型

cpp 复制代码
#include <arpa/inet.h>

int inet_pton(int af, const char *src, void *dst);
  • 参数
    • af(地址族):指定地址类型
      • AF_INET
      • AF_INET6
    • src(源字符串):C 字符串形式的 IP 地址;
    • dst(目标内存地址):转换后的结果将存放到此结构体地址中;
      • IPv4:&sockaddr_in.sin_addr
      • IPv6:&sockaddr_in6.sin6_addr
  • 返回值
    • 成功: 1;
    • **失败: **
      • IP地址非法:0
      • 出错:-1, 并设置 errno

7. inet_ntop(库函数)

作用:将一个网络字节序的二进制 IP 地址(IPv4 或 IPv6)转换为字符串形式(点分/冒号分隔)。是inet_ntoa()的可重入(线程安全)版本。

函数原型

cpp 复制代码
#include <arpa/inet.h>

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
  • 参数
    • af(地址族):指定地址类型
      • AF_INET
      • AF_INET6
    • src(源地址):指向存放网络字节序IP地址的内存(结构体);
      • IPv4:&sockaddr_in.sin_addr
      • IPv6:&sockaddr_in6.sin6_addr
    • dst(目标缓冲区):用于保存转换后的字符串;
      • 一般定义为 char buf[INET_ADDRSTRLEN](IPv4)或 char buf[INET6_ADDRSTRLEN](IPv6)。
    • size(缓冲区大小)
  • 返回值

    • 成功: 返回指向 dst 的指针(即转换后的字符串);

    • **失败:errno置为EAFNOSUPPORT 或 ENOSPC) **


8. recvfrom(系统调用)

作用: 从一个 UDP 套接字中接收数据,同时获取发送方(客户端)的地址信息。(常用于 UDP 服务器端接收消息)

函数原型

cpp 复制代码
#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:一般填 0 (阻塞IO,如果对方不发数据,该函数(进程)就会一直阻塞,等同与scanf; MSG_DONTWAIT 表示非阻塞接收);
    • src_addr:
      • 一个 struct sockaddr_in 类型的指针,用于存储发送方的地址信息(IP + 端口);
      • 如果不关心发送方,可以填 NULL;
    • addrlen:
      • 指向一个socklen_t类型的变量,socklen_t实际上是一个整数类型(可跨平台);
      • addrlen既是输入参数, 又是输出参数: 初始时应设置为 sizeof(struct sockaddr_in); 函数返回时,会被填入实际的地址长度.
  • 返回值
    • 成功: 返回接收到的字节数(ssize_t是有符号整数long int);
    • 失败: 返回 -1,并设置 errno.

9. sendto(系统调用)

作用: 向指定的目标地址发送一块数据(UDP 无连接发送)。(常用于 UDP 客户端发送,或服务器向收到的源地址回发数据)

函数原型

cpp 复制代码
#include <sys/types.h>
#include <sys/socket.h>

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);
  • 参数
    • sockfd:套接字文件描述符;
    • buf:待发送数据的起始地址;
    • len:待发送数据的字节数;
    • flags:发送标志 (一般填 0 阻塞发送; 常见可选:MSG_DONTWAIT 非阻塞发送,MSG_NOSIGNAL 发送失败不触发 SIGPIPE 等);
    • dest_addr:目标地址结构体指针
      • 服务器回包可直接用 recvfrom 得到的 src_addr 作为 dest_addr
    • addrlen:地址结构体(dest_addr)大小
  • 返回值
    • 成功: 返回实际发送的字节数;
    • 失败: 返回 -1,并设置 errno.

二.命令补充

1. netstat(系统命令)

作用: 显示网络连接、路由表、接口状态、端口监听情况等网络统计信息。

命令格式

shell 复制代码
netstat [选项]
  • 常用选项组合

    • -a :显示所有连接和监听端口(all)
    • -l:仅显示处于监听(listening)状态的端口
    • -n:以数字形式显示 IP 地址和端口号(不解析主机名)
    • -u:仅显示 UDP 协议连接
    • -t:仅显示 TCP 协议连接
    • -p:显示对应的进程号与程序名(需要 root 权限)
  • 常见用法举例

    shell 复制代码
    netstat -anup

    作用:

    显示系统中所有 UDP 套接字(包括监听与已连接的),并显示使用这些端口的程序信息。

  • 输出示例

    shell 复制代码
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    udp        0      0 0.0.0.0:8080           0.0.0.0:*                           2314/udpserver
    udp        0      0 127.0.0.1:53           0.0.0.0:*                           924/dnsmasq
    udp6       0      0 :::68                  :::*                                652/systemd-network
  • 字段说明

    字段 含义
    Proto 协议类型(TCP/UDP)
    Recv-Q 接收队列中尚未被应用读取的字节数
    Send-Q 发送队列中尚未发送的字节数
    Local Address 本地 IP + 端口号 (0.0.0.0 表示监听所有网卡)
    Foreign Address 远端 IP + 端口号(UDP 无连接时显示为 *)
    State 套接字状态(UDP 无状态,通常为空)
    PID/Program name 占用该端口的进程号及程序名
  • 常用变体

    命令 作用
    netstat -anup 查看所有 UDP 端口与进程信息
    netstat -antp 查看所有 TCP 连接与进程信息

三.知识补充

1. 客户端bind问题

  1. 问题:client要不要bind?需要bind.

    client要不要显式的bind?不要!首次发送消息,OS会自动给client进行bind,OS知道IP,端口号采用随机端口号的方式

  2. 为什么?一个端口号,只能被一个进程bind,为了避免client端口冲突

    client端的端口号是几,不重要,只要是唯一的就行!

2.本地环回

本地环回:127.0.0.1, 要求客户端和服务端必须在一台服务器上, 表明我们是本地通信, 客户端发送的数据不会被推送到网络,而是在OS内部转一圈直接交给对应的服务器端。经常用来进行网络代码的测试

3. 三个现象

(1).bind公网iP -- 不能

公网IP其实没有配置到你的IP上。公网IP无法被直接bind!

(2).bind 127.0.0.1||内网IP -- ok
(3).server bind内网IP,但是用127.0.0.1访问---访问不 (反过来也不行)

如果我们显式的进行地址bind,client未来访问的时候,就必须使用server端bind的地址信息!

  • 如何跨网络访问?

    server,不建议手动bind特定的

    cpp 复制代码
    struct sockaddr_in local;
    local.sin_addr.s_addr = INADDR_ANY; //任意IP地址bind
    
    /* Address to accept any incoming messages.  */
    //#define	INADDR_ANY		((in_addr_t) 0x00000000)

    这样的好处是: 只要发给这台机器bind指定的端口号的报文都可以接受。

相关推荐
Empty_7774 小时前
Python编程之常用模块
开发语言·网络·python
m0_611779965 小时前
MQTT和WebSocket的差别
网络·websocket·网络协议
AORO20255 小时前
防爆手机与普通手机有什么区别?防爆手机哪个牌子好?
运维·服务器·网络·5g·智能手机·信息与通信
望获linux5 小时前
【实时Linux实战系列】使用 u-trace 或 a-trace 进行用户态应用剖析
java·linux·前端·网络·数据库·elasticsearch·操作系统
对岸住着星星5 小时前
断电重启后自动重连WiFi并分配固定IP的Armbian脚本
服务器·网络·tcp/ip
red watchma5 小时前
Xshell->MCU Ymodem协议实现
网络·单片机·嵌入式硬件
sky北城6 小时前
读书笔记整理--网络学习与概念整合
网络·智能路由器
极客范儿6 小时前
新华三H3CNE网络工程师认证—OSPF多区域概念与配置
网络·智能路由器
望获linux6 小时前
【实时Linux实战系列】FPGA 与实时 Linux 的协同设计
大数据·linux·服务器·网络·数据库·fpga开发·操作系统