手写Linux C UDP通信

复制代码
int main(int argc,char const *argv[])
{
    int socket_fd=socket(AF_INET,SOCK_DGRAM,0);
    if(-1==socket_fd){
        perror("socket error");
        return -1;
    }
    struct sockaddr_in dest_addr={0};
    int addr_len=sizeof(struct sockaddr_in);
    dest_addr.sin_family=AF_INET;
    dest_addr.sin_port=htons(65000);
    dest_addr.sin_addr.s_addr=inet_addr("192.168.10.100");


    char *msg=calloc(128,1);
    struct sockaddr_in from_addr;
    

    while(1){
        printf("请输入:\n");
      fgets(msg,128,stdin);
    int ret_val=sendto(socket_fd,msg,strlen(msg),0,
    (struct sockaddr *)&dest_addr,addr_len);
    if(-1==ret_val){
        perror("send to error");
        continue;
    }
    else{
        printf("send succeed, sent %d bytes\n", ret_val);
    }


    }
    return 0;
    
}

好,这段是UDP 客户端发送程序 ,我给你来一份老师上课那种逐行注释版,顺便把每一步"在干嘛 + 为啥要这么干"讲清楚。你照着看一遍,UDP 就基本通了 👍


复制代码
int main(int argc, char const *argv[])
{
  • 程序入口

  • argc / argv:命令行参数,这个程序里没用到


复制代码
    int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
  • 创建一个 UDP socket

  • AF_INET:IPv4

  • SOCK_DGRAM:数据报 → UDP

  • 0:协议号,UDP 会自动匹配

📌 一句话:向操作系统申请一个"UDP 通信的文件描述符"


复制代码
    if (-1 == socket_fd) {
        perror("socket error");
        return -1;
    }
  • 判断 socket 是否创建成功

  • -1 说明失败

  • perror 会打印系统错误原因


复制代码
    struct sockaddr_in dest_addr = {0};
  • 定义 目标地址结构体

  • {0}:全部清零(好习惯,防止脏数据)


复制代码
    int addr_len = sizeof(struct sockaddr_in);
  • 地址结构体长度

  • sendto() 需要用到


复制代码
    dest_addr.sin_family = AF_INET;
  • 地址类型:IPv4

  • 必须和 socket(AF_INET, ...) 对应


复制代码
    dest_addr.sin_port = htons(65000);
  • 设置 目标端口号

  • htons:主机字节序 → 网络字节序

    (网络通信统一使用大端序

📌 如果你不 htons,对方收到的端口会是错的


复制代码
    dest_addr.sin_addr.s_addr = inet_addr("192.168.10.100");
  • 设置 目标 IP 地址

  • inet_addr:把字符串 IP 转成网络可用的 32 位整数

📌 这里就是:要把 UDP 数据发给谁


复制代码
    char *msg = calloc(128, 1);
  • 分配 128 字节的发送缓冲区

  • calloc

    • 分配内存

    • 自动清零

  • 用来存你输入的字符串


复制代码
    struct sockaddr_in from_addr;
  • 定义"对方地址"

  • ⚠️ 这里其实没用到

  • 一般用于 recvfrom() 接收数据时保存"谁发来的"


复制代码
    while (1) {
  • 死循环

  • 表示可以一直发送数据


复制代码
        printf("请输入:\n");
  • 提示用户输入

复制代码
        fgets(msg, 128, stdin);
  • 从标准输入读一行

  • 最多 127 个字符 + \0

  • 会把回车 \n 一起读进来

📌 比 scanf 安全,不容易溢出


复制代码
        int ret_val = sendto(
            socket_fd,
            msg,
            strlen(msg),
            0,
            (struct sockaddr *)&dest_addr,
            addr_len
        );

这是核心一句,拆开说:

参数 含义
socket_fd UDP socket
msg 要发送的数据
strlen(msg) 数据长度
0 flags,一般填 0
&dest_addr 目标 IP + 端口
addr_len 地址长度

📌 UDP 特点

  • 不用 connect

  • 每次 sendto 都要带目标地址


复制代码
        if (-1 == ret_val) {
            perror("send to error");
            continue;
        }
  • 发送失败

  • 打印错误

  • continue:跳过本轮,继续下一次发送


复制代码
        else {
            printf("send succeed, sent %d bytes\n", ret_val);
        }
  • 发送成功

  • ret_val实际发送的字节数


复制代码
    }
  • while(1) 结束(但实际上不会出来)

复制代码
    return 0;
}
  • 正常结束(理论上执行不到)

🔥 整体一句话总结

这是一个:

UDP 客户端程序

不断从键盘读字符串 → 通过 UDP 发给
192.168.10.100 : 65000


⚠️ 几个你迟早会问的问题(我先帮你答)

1️⃣ 为什么不用 bind()

  • 客户端一般不需要固定端口

  • 系统会自动分配一个临时端口


2️⃣ 对方怎么接收?

对方要:

复制代码
socket(AF_INET, SOCK_DGRAM, 0);
bind(65000);
recvfrom();

3️⃣ 为什么 UDP 不用 connect?

  • UDP 无连接

  • 每个包都是"独立快递"


4️⃣ from_addr 为什么没用?

  • 因为你只发不收

  • 如果加 recvfrom(),它就派上用场了


复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main(int argc,char const *argv[])
{
    int socket_fd=socket(AF_INET,SOCK_DGRAM,0);
    if(-1==socket_fd){
        perror("socket error");
        return -1;
    }
    struct sockaddr_in my_addr={0};
    int addr_len=sizeof(struct sockaddr_in);
    my_addr.sin_family=AF_INET;
    my_addr.sin_port=htons(65000);
    my_addr.sin_addr.s_addr=inet_addr("192.168.10.100");




   
    int ret_val=bind(socket_fd,(struct sockaddr*)&my_addr,
                    sizeof(my_addr));
    if(-1==ret_val){
        perror("bind error");
        return -1;

    }
    char *msg=calloc(128,1);
    struct sockaddr_in from_addr;
    

    while(1){
        ret_val=recvfrom(socket_fd,msg,128,0,(struct sockaddr *)&from_addr,&addr_len);
        if(ret_val==-1){
            printf("recv error");
            continue;
        }
        else{
            printf("recv succeed,msg:%s\n",msg);
        }
    }


    return 0;
}

好,这个就是UDP 接收端(服务器)了,而且你这个写法基本是对的 👍

我继续按你要的------逐行 + 顺带点坑位的"老师黑板版"来讲。


一、整体一句话先给你

这个程序做的事是:
绑定本机 192.168.10.100:65000,不断接收 UDP 数据并打印出来


二、逐行注释(重点解释"为什么")

复制代码
int main(int argc,char const *argv[])
{
  • 程序入口

  • argc / argv 依然没用到


复制代码
    int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
  • 创建 UDP socket

  • IPv4 + UDP


复制代码
    if (-1 == socket_fd) {
        perror("socket error");
        return -1;
    }
  • 创建失败直接退出

复制代码
    struct sockaddr_in my_addr = {0};
  • 定义 本地地址结构体

  • {0}:清零,防止未初始化字段


复制代码
    int addr_len = sizeof(struct sockaddr_in);
  • 地址结构体长度

  • ⚠️ 后面 recvfrom() 里会被当成 "输入 + 输出参数"


复制代码
    my_addr.sin_family = AF_INET;
  • IPv4

复制代码
    my_addr.sin_port = htons(65000);
  • 绑定的本地端口

  • 必须 htons


复制代码
    my_addr.sin_addr.s_addr = inet_addr("192.168.10.100");
  • 绑定的 本机 IP

  • ⚠️ 这个 IP 必须是你这台机器真实拥有的

👉 如果写错:

  • bind error

  • 或者客户端发了你也收不到


复制代码
    int ret_val = bind(
        socket_fd,
        (struct sockaddr*)&my_addr,
        sizeof(my_addr)
    );
  • 把 socket 和本地 IP + 端口绑定

  • UDP 接收端 必须 bind

  • 不 bind,数据不知道往哪儿送


复制代码
    if (-1 == ret_val) {
        perror("bind error");
        return -1;
    }
  • 绑定失败(端口占用 / IP 不存在 / 权限问题)

复制代码
    char *msg = calloc(128, 1);
  • 接收缓冲区

  • 128 字节

  • 清零,方便直接当字符串打印


复制代码
    struct sockaddr_in from_addr;
  • 保存 发送方地址

  • recvfrom() 会把"谁发来的"填进这里


复制代码
    while (1) {
  • 死循环接收数据

复制代码
        ret_val = recvfrom(
            socket_fd,
            msg,
            128,
            0,
            (struct sockaddr *)&from_addr,
            &addr_len
        );

🔥 核心函数 recvfrom

参数 含义
socket_fd UDP socket
msg 接收缓冲区
128 最大接收字节数
0 flags
from_addr 对方 IP + 端口(输出)
&addr_len 地址结构体长度

📌 阻塞行为

  • 没数据 → 程序卡在这儿等

  • 一来数据 → 立刻返回


复制代码
        if (ret_val == -1) {
            printf("recv error");
            continue;
        }
  • 接收失败

  • 正常情况下很少失败


复制代码
        else {
            printf("recv succeed, msg:%s\n", msg);
        }
  • 接收成功

  • 直接按字符串打印

📌 ret_val收到的字节数


复制代码
    }
  • 无限接收

复制代码
    return 0;
}
相关推荐
明天就是Friday2 小时前
(五)Linux 调度器 - CFS调度器
linux·linux内核·linux 调度器
水饺编程2 小时前
第4章,[标签 Win32] :绘制信息结构
c语言·c++·windows·visual studio
阿拉伯柠檬2 小时前
网络层与网络层协议IP(一)
linux·网络·网络协议·tcp/ip·面试
lcreek2 小时前
Linux 信号机制详解:从硬件异常到安全编程实践
linux·系统编程
南 阳2 小时前
Python从入门到精通day10
linux·windows·python
xdpcxq10292 小时前
Apache 详解 在 Ubuntu 24 中安装和配置 Apache
linux·ubuntu·apache
General_G2 小时前
irobot_benchmark的编译和使用
linux·中间件·机器人·ros2
独隅2 小时前
Linux 正则表达式 的简介
linux·mysql·正则表达式
chinesegf2 小时前
虚拟机ubuntu中磁盘满了 + 镜像损坏,如何解决
linux·运维·ubuntu