linux——UDP编程

UDP 是发短信,TCP 是打电话。

  • TCP 打电话:必须先拨号(connect)、接通(accept),才能说话,全程不断开。
  • UDP 发短信 :不需要接通,直接填对方号码 → 发送 ,对方直接收。没有连接、没有握手、没有断开。

1、UDP 最核心特点

  1. 无连接不用 listen、不用 accept、不用 connect(服务端不用)。
  2. 数据报模式一条一条发,一条一条收。
  3. 不可靠发出去不管对方收没收到。
  4. 速度快
  5. 支持一对多、多对多(广播、组播)

2、UDP 服务端 固定 4 步

1. socket()

创建 UDP 套接字

复制代码
socket(AF_INET, SOCK_DGRAM, 0);
  • SOCK_DGRAM → 代表 UDP

2. bind()

绑定 IP 和端口(和 TCP 一模一样)告诉系统:我要在这个端口收数据。

3. recvfrom()

阻塞接收数据 谁发来都行,来了就收。同时获取客户端 IP + 端口

4. close()

关闭套接字。

3、UDP 客户端 固定 3 步

  1. socket()
  2. sendto () → 直接发给服务器
  3. close()

**客户端甚至可以不用 bind!**系统自动分配端口。

4、UDP 最重要的两个函数

1. recvfrom(服务端收数据)

复制代码
recvfrom(
    sockfd,
    buf,
    len,
    0,
    (struct sockaddr *)&client_addr,  // 客户端地址
    &addrlen
);

作用:阻塞等数据,收到数据,同时知道谁发的。

2. sendto(客户端发数据)

复制代码
sendto(
    sockfd,
    buf,
    len,
    0,
    (struct sockaddr *)&server_addr,  // 目的地址
    addrlen
);

作用:直接把数据发给目标 IP + 端口

5、UDP 服务端流程

plaintext

复制代码
socket → bind → 循环 recvfrom 收数据 → 处理

没有 listen! 没有 accept! 没有连接!

谁发数据给这个端口,服务器就收谁的。

6、TCP vs UDP 函数对比

步骤 TCP UDP
创建 socket(...SOCK_STREAM...) socket(...SOCK_DGRAM...)
绑定 bind bind
监听 listen
连接 accept / connect
收数据 read recvfrom
发数据 write sendto

服务器端

复制代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<strings.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>


#define SERV_PORT 5001
#define BUFSIZE 1024
#define QUIT_STR "QUIT"
int main()
{
	int fd = -1;
	struct sockaddr_in sin;
	//1.socket
	fd = socket(AF_INET,SOCK_DGRAM,0);
	if(fd < 0)
	{
		perror("socket");
		exit(1);
	}
	int b_reuser = 1;
	
	setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuser,sizeof(int));

	//2.bind
	bzero(&sin,sizeof(sin));

	sin.sin_family = AF_INET;
	sin.sin_port = htons(SERV_PORT);
	sin.sin_addr.s_addr = INADDR_ANY;

	if(bind(fd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		perror("bind");
		exit(1);
	}
    
    //3.recvfrom
	char buf[BUFSIZE];
	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);
	while(1)
	{
		bzero(buf,BUFSIZE);
		if(recvfrom(fd,buf,BUFSIZE-1,0,(struct sockaddr*)&cin,&addrlen) < 0)
		{
			perror("recvfrom");
			continue;
		}
		char ipv4_addr[16];
		if(!inet_ntop(AF_INET,(void *)&cin.sin_addr,ipv4_addr,sizeof(cin)))
		{
			perror("inet_ntop");
			exit(1);
		}
		printf("receive from(%s,%d),data:%s\n",ipv4_addr,ntohs(cin.sin_port),buf);

		if(!strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)))
		{
			printf("client(%s:%d)is exiting!\n",ipv4_addr,ntohs(cin.sin_port));
		}
	}
	close(fd);
	return 0;
}

①socket () 创建 UDP 套接字

复制代码
fd = socket(AF_INET, SOCK_DGRAM, 0);

重点:

  • AF_INET:IPv4
  • SOCK_DGRAM :数据报 → UDP
  • 之前 TCP 是 SOCK_STREAM

这一句决定了是 UDP 还是 TCP

②setsockopt()端口复用

复制代码
int b_reuser = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuser, sizeof(int));

作用:

允许程序重启后,立刻绑定同一个端口不加这句,程序退出后,端口会被占用几十秒。

③bind()绑定IP+端口

复制代码
sin.sin_family = AF_INET;
sin.sin_port = htons(SERV_PORT);
sin.sin_addr.s_addr = htonl(INADDR_ANY);

bind(fd, (struct sockaddr*)&sin, sizeof(sin));

和 TCP 完全一样!

  • 绑定端口 5001
  • 绑定所有网卡 INADDR_ANY

④最核心:UDP 怎么收数据?UDP 没有 accept!直接收!

复制代码
while(1)
{
    bzero(buf, BUFSIZE);

    // 阻塞等待接收 UDP 数据报
    recvfrom(
        fd,
        buf,
        BUFSIZE-1,
        0,
        (struct sockaddr*)&cin,
        &addrlen
    );
}

recvfrom () 函数

函数作用:

阻塞等待客户端发来 UDP 数据报 谁发来的?发来什么内容?全部拿到!

参数:

fdUDP 套接字

buf接收数据的缓冲区

BUFSIZE-1最大接收长度

0默认为 0

&cin 用来保存客户端的 IP 和端口

&addrlen地址长度

⑤接收后打印客户端信息

复制代码
inet_ntop(AF_INET, &cin.sin_addr, ipv4_addr, sizeof(ipv4_addr));

printf("receive from(%s,%d), data:%s\n",
       ipv4_addr,
       ntohs(cin.sin_port), 
       buf);

⑥判断退出

复制代码
if(!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR)))
{
    printf("client exiting\n");
}

收到 QUIT 就提示客户端退出。

客户端

复制代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<strings.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>

#define SREV_PORT 5001
#define SERV_IP_ADDR "192.168.88.129"
#define QUIT_STR "QUIT"
#define BUFSIZE 1024

int main(int argc,char **argv)
{
	if(argc != 3)
	{
		exit(1);
	}
	int fd = socket(AF_INET,SOCK_DGRAM,0);
	if(fd < 0)
	{
		perror("socket");
		exit(1);
	}
	int port = -1;
	port = atoi(argv[2]);

	struct sockaddr_in sin;

	bzero(&sin,sizeof(sin));

	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);
	sin.sin_addr.s_addr = inet_addr(argv[1]);

	char buf[BUFSIZE];
	while(1)
	{
		if(fgets(buf,BUFSIZE-1,stdin) == NULL)
		{
			perror("fgets");
			continue;
		}

		sendto(fd,buf,strlen(buf),0,(struct sockaddr*)&sin,sizeof(sin));

		if(!strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)))
		{
			printf("client is exited!\n");
			break;
		}
	}

	close(fd);

	return 0;
}

①主函数&命令行参数

复制代码
int main(int argc,char **argv)
{
	if(argc != 3)
	{
		exit(1);
	}

作用:必须传入 2 个参数:服务器 IP + 端口

②创建UDP套接字

复制代码
int fd = socket(AF_INET,SOCK_DGRAM,0);
if(fd < 0)
{
    perror("socket");
    exit(1);
}

重点:

  • AF_INET:IPv4
  • SOCK_DGRAMUDP 数据报 (TCP 是 SOCK_STREAM

③读取命令行端口

复制代码
int port = atoi(argv[2]);

把命令行的字符串端口→转成数字

④配置服务器地址(填短信收件人)

复制代码
struct sockaddr_in sin;
bzero(&sin,sizeof(sin));

sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = inet_addr(argv[1]);

作用:

填写目标服务器的 IP 和端口。

  • sin:服务器地址结构体
  • argv[1]:服务器 IP
  • port:服务器端口

⑤循环发送数据

复制代码
char buf[BUFSIZE];
while(1)
{
    // 从键盘读入数据
    if(fgets(buf,BUFSIZE-1,stdin) == NULL)
    {
        perror("fgets");
        continue;
    }

    // 发送数据(UDP 核心函数)
    sendto(fd,buf,strlen(buf),0,(struct sockaddr*)&sin,sizeof(sin));

    // 如果输入 QUIT,退出
    if(!strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)))
    {
        printf("client is exited!\n");
        break;
    }
}

sendto()函数

复制代码
sendto(
    fd,         // UDP 套接字
    buf,        // 要发送的数据
    strlen(buf),// 数据长度
    0,          // 默认为 0
    (struct sockaddr*)&sin,  // 服务器地址
    sizeof(sin) // 地址长度
);

作用:

直接把数据发送给指定的服务器! 不需要连接!不需要 connect!

参数:

fdUDP 套接字

buf接收数据的缓冲区

BUFSIZE-1最大接收长度

0默认为 0

&cin 用来保存客户端的 IP 和端口

&addrlen地址长度

⑥关闭套接字&退出

复制代码
close(fd);
return 0;

执行结果

相关推荐
华普微HOPERF2 小时前
如何善用Sub-GHz射频芯片,打造低功耗、高稳定的IoT通信网络?
网络·物联网
Amelio_Ming2 小时前
linux内存管理-页面回收之内核线程 kswapd (二)
linux·服务器
忙什么果2 小时前
海洋遥感论文中常说的:in-situ数据和proxy
linux·运维·服务器
360智汇云2 小时前
从安全组到网络ACL:企业级网络隔离能力的进阶方案
网络·安全
徒 花2 小时前
HCIP学习09 重发布(路由引入)+ 路由策略
网络·学习·hcip
bukeyiwanshui2 小时前
20260413 条件测试常用语法
linux
IP老炮不瞎唠3 小时前
如何利用住宅代理提升Capsolver验证码通过率
网络
齐潇宇3 小时前
Tomcat服务
linux·运维·网络·http·tomcat·web应用
虎头金猫3 小时前
GodoOS是一款轻量级云端办公系统,整合Word、Excel、PPT等常用工具,支持Docker 一键部署,随时随地远程办公
运维·服务器·网络·程序人生·docker·容器·职场和发展