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;
}