4058 字
20 分钟
网络套接字:报式套接字&UDP【Unix编程】

报式套接字(Datagram Socket)是一种无连接的网络通信接口,使用UDP协议传输数据,数据以独立的数据包形式发送,不保证数据的顺序和可靠性,但具有较低的延迟和开销。

使用流程#

Server端流程:#

  1. 创建套接字:使用socket(AF_INET, SOCK_DGRAM, 0)创建UDP套接字
  2. 绑定地址:使用bind()函数绑定服务器的IP地址和端口号
  3. 接收数据:使用recvfrom()接收客户端发送的数据包
  4. 发送响应:使用sendto()向客户端发送数据(可选)
  5. 关闭套接字:使用close()关闭套接字

Client端流程:#

  1. 创建套接字:同样使用socket(AF_INET, SOCK_DGRAM, 0)创建UDP套接字
  2. 发送数据:使用sendto()向服务器指定的地址和端口发送数据包
  3. 接收响应:使用recvfrom()接收服务器的响应数据(可选)
  4. 关闭套接字:使用close()关闭套接字
NOTE

当UDP客户端不调用bind()时,操作系统会在第一次调用sendto()时自动为其随机分配


函数#

recvfrom#

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);

作用
recvfrom 用于从套接字接收消息。它可以用于接收无连接和面向连接套接字上的数据。该函数将接收到的消息放入缓冲区 buf 中。

参数

  • int sockfd:指定从哪个套接字接收数据。
  • void *buf:指向缓冲区的指针,用于存放接收到的数据。
  • size_t len:指定缓冲区的大小。
  • int flags:指定接收操作的标志。
选项说明
MSG_CMSG_CLOEXEC(仅限 recvmsg) 设置通过 UNIX 域文件描述符接收的文件描述符的 close-on-exec 标志。
MSG_DONTWAIT启用非阻塞操作;如果操作会阻塞,则调用失败,并返回错误 EAGAINEWOULDBLOCK
MSG_ERRQUEUE指定应从套接字错误队列中接收排队的错误。
MSG_OOB请求接收带外数据,这些数据不会在正常数据流中接收。
MSG_PEEK使接收操作返回接收队列开头的数据,而不从队列中删除该数据。
MSG_TRUNC返回数据包或数据报的真实长度,即使它比传递的缓冲区长。
MSG_WAITALL请求操作阻塞直到满足完整请求。
  • struct sockaddr *src_addr:如果此参数不为 NULL,并且底层协议提供了消息的源地址,则将源地址放入此参数指向的缓冲区中。
  • socklen_t *addrlen:这是一个值-结果参数。在调用之前,应将其初始化为与 src_addr 关联的缓冲区的大小。返回时,addrlen 会更新为包含源地址的实际大小。

返回值

  • 成功时返回接收的字节数。
  • 失败时返回 -1,并设置 errno 指示错误类型。

sendto#

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);

作用
sendto 系统调用用于在套接字上发送消息。与 send 不同,sendto 可以在未连接的套接字上使用,并通过 dest_addraddrlen 参数指定目标地址。对于面向连接的套接字(如 SOCK_STREAMSOCK_SEQPACKET),如果套接字已连接,则 dest_addraddrlen 参数会被忽略;

参数

  • int sockfd:发送套接字的文件描述符。
  • const void *buf:指向要发送的数据缓冲区的指针。
  • size_t len:要发送的数据长度(以字节为单位)。
  • int flags:控制发送行为的标志位,可以通过按位或组合多个标志。
  • const struct sockaddr *dest_addr:目标地址结构体的指针。
  • socklen_t addrlen:目标地址结构体的大小。

flags 参数选项

选项说明
MSG_CONFIRM通知链路层已收到对方的成功响应,适用于 SOCK_DGRAMSOCK_RAW 套接字。
MSG_DONTROUTE不使用网关发送数据包,仅发送到直接连接的主机,通常用于诊断或路由程序。
MSG_DONTWAIT启用非阻塞操作;如果操作会阻塞,则返回 EAGAINEWOULDBLOCK
MSG_EOR终止一条记录仅适用于支持此特性的套接字类型,如 SOCK_SEQPACKET
MSG_MORE表示调用者还有更多数据要发送。对于 TCP 套接字,可实现与 TCP_CORK 类似的效果;对于 UDP 套接字,可将多次调用的数据打包成一个数据报。
MSG_NOSIGNAL发送数据时,如果对端关闭了连接,不生成 SIGPIPE 信号。
MSG_OOB在支持带外数据的套接字上发送带外数据(例如 SOCK_STREAM)。
MSG_FASTOPEN尝试使用 TCP Fast Open(RFC7413),在 SYN 包中发送数据。

返回值

  • 成功时返回已发送的字节数。
  • 失败时返回 -1,并设置 errno 指示错误类型。

示例Get Score#

这是一个基于UDP协议的简单客户端-服务器通信程序,用于传输学生姓名和成绩信息。 客户端发送学生信息(姓名、语文成绩、数学成绩)到服务器,服务器接收并显示这些信息。

client.c

#include "arpa/inet.h"
#include "proto.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "sys/socket.h"
int main(int argc, char *argv[]) {
if (argc != 4) {
fprintf(stderr, "ERROR!Usage:<name> <chinese> <math>");
exit(1);
}
struct msg_st msg;
strcpy(msg.name, argv[1]);
msg.chinese = atoi(argv[2]);
msg.math = atoi(argv[3]);
int sd;
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
perror("socket()");
exit(1);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(RCVPORT));
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
socklen_t socklen;
socklen = sizeof(addr);
if (sendto(sd, &msg, sizeof(msg), 0, (void *)&addr, socklen) < 0) {
perror("sendto()");
exit(1);
}
return 0;
}

server.c

#include "proto.h"
#include "stdio.h"
#include "stdlib.h"
#include "sys/socket.h"
#include <arpa/inet.h>
#define BUFFSIZE 1024
#define IPSIZE 24
int main() {
int sd;
struct sockaddr_in laddr;
struct sockaddr_in raddr;
laddr.sin_family = AF_INET;
laddr.sin_port = htons(atoi(RCVPORT));
inet_pton(AF_INET, "0.0.0.0", &laddr.sin_addr);
struct msg_st *rbuf;
rbuf = malloc(sizeof(struct msg_st));
if (rbuf == NULL) {
perror("malloc()");
exit(1);
}
socklen_t raddr_len;
raddr_len = sizeof(raddr);
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
perror("socket()");
exit(1);
}
if (bind(sd, (struct sockaddr *)&laddr, sizeof(struct sockaddr_in)) < 0) {
perror("bind()");
exit(1);
}
char ipstr[IPSIZE];
while (1) {
recvfrom(sd, rbuf, sizeof(rbuf), 0, (void *)&raddr, &raddr_len);
inet_ntop(AF_INET, &raddr.sin_addr, ipstr, raddr_len);
printf("--MESSAGE FROM %s:%d--\n", ipstr, ntohs(raddr.sin_port));
printf("NAME=%s\n", rbuf->name);
printf("CHINESE=%d\n", rbuf->chinese);
printf("MATH=%d\n", rbuf->math);
}
}

proto.h

#pragma once
#define NAMESIZE 16
#define RCVPORT "1989"
#include "stdint.h"
struct msg_st {
uint8_t name[NAMESIZE];
uint32_t math;
uint32_t chinese;
} __attribute__((packed));

运行结果:

动态数据报#

数据报的结构和大小在运行时可变。对于可变长的数据报成为动态数据报。动态数据报可以更灵活地适应不同类型的数据负载。

对于一个变长结构体

struct msg_st {
uint32_t math;
uint32_t chinese;
uint8_t name[];
} __attribute__((packed));

UDP数据包推荐长度为512,UPD报头长度为8。 uint8_t name[]的大小可以定义为: UPD推荐长度-UPD报头长度-固定成员大小

#define NAMEMAX 512-8-8

对于修改后的程序,client(发送方),创建一个变长结构体进行发送。
server(接收方),按照最大长度进行接收。 proto.h

#pragma once
#define NAMESIZE 16
#define RCVPORT "1989"
#define NAME_MAX (512 - 8 - 8)
#include "stdint.h"
struct msg_st {
uint32_t math;
uint32_t chinese;
uint8_t name[];
} __attribute__((packed));

client.c

#include "arpa/inet.h"
#include "proto.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "sys/socket.h"
int main(int argc, char *argv[]) {
if (argc != 4) {
fprintf(stderr, "ERROR!Usage:<name> <chinese> <math>");
exit(1);
}
if (strlen(argv[1]) > NAME_MAX) {
fprintf(stderr, "name is too long!\n");
exit(1);
}
int size = sizeof(struct msg_st) + strlen(argv[1]);
struct msg_st *msg_p;
msg_p = malloc(size);
if (msg_p == NULL) {
perror("malloc");
exit(1);
}
strcpy(msg_p->name, argv[1]);
msg_p->chinese = atoi(argv[2]);
msg_p->math = atoi(argv[3]);
int sd;
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
perror("socket()");
exit(1);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(RCVPORT));
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
socklen_t socklen;
socklen = sizeof(addr);
if (sendto(sd, msg_p, size, 0, (void *)&addr, socklen) < 0) {
perror("sendto()");
exit(1);
}
free(msg_p);
return 0;
}

server.c

#include "proto.h"
#include "stdio.h"
#include "stdlib.h"
#include "sys/socket.h"
#include <arpa/inet.h>
#define BUFFSIZE 1024
#define IPSIZE 24
int main() {
int sd;
struct sockaddr_in laddr;
struct sockaddr_in raddr;
laddr.sin_family = AF_INET;
laddr.sin_port = htons(atoi(RCVPORT));
inet_pton(AF_INET, "0.0.0.0", &laddr.sin_addr);
int size = sizeof(struct msg_st) + NAME_MAX;
struct msg_st *rbuf;
rbuf = malloc(size);
if (rbuf == NULL) {
perror("malloc()");
exit(1);
}
socklen_t raddr_len;
raddr_len = sizeof(raddr);
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
perror("socket()");
exit(1);
}
if (bind(sd, (struct sockaddr *)&laddr, sizeof(struct sockaddr_in)) < 0) {
perror("bind()");
exit(1);
}
char ipstr[IPSIZE];
while (1) {
recvfrom(sd, rbuf, size, 0, (void *)&raddr, &raddr_len);
inet_ntop(AF_INET, &raddr.sin_addr, ipstr, raddr_len);
printf("--MESSAGE FROM %s:%d--\n", ipstr, ntohs(raddr.sin_port));
printf("NAME=%s\n", rbuf->name);
printf("CHINESE=%d\n", rbuf->chinese);
printf("MATH=%d\n", rbuf->math);
}
}

运行结果: 相比原固有长度的数据报,该方案可以适应不同名字的大小,最大不超过NAMEMAX。

多点通信#

相比流式套接字点对点通信,报式套接字可以做到多点通信。

setsockopt#

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

作用 setsockopt 用于设置与套接字关联的各种选项。这些选项可以存在于多个协议级别,但它们始终存在于最高层的套接字级别。

参数

  • int sockfd:指定套接字的文件描述符。
  • int level:指定选项所在的协议级别。要操作套接字 API 级别的选项,应将此参数设置为 SOL_SOCKET。对于其他级别的选项,应提供控制该选项的相应协议的协议号。
  • int optname:指定要设置的选项名称。
  • const void *optval:指向包含选项值的缓冲区。
  • socklen_t optlen:指定 optval 缓冲区的大小。
选项说明
SOL_SOCKET表示选项位于套接字 API 级别。
IPPROTO_TCP表示选项由 TCP 协议控制。
IPPROTO_IP表示选项由 IP 协议控制。

返回值

  • 成功时返回 0。
  • 失败时返回 -1,并设置 errno 指示错误类型。

SOCKET操作选项

选项名称简介说明
SO_ACCEPTCONN接受连接状态返回值指示套接字是否已标记为通过 listen(2) 接受连接。0表示非监听套接字,1表示监听套接字(只读)
SO_ATTACH_FILTER附加BPF过滤器将经典BPF程序附加到套接字作为传入数据包的过滤器(Linux 2.2+)
SO_ATTACH_BPF附加扩展BPF将扩展BPF程序附加到套接字(Linux 3.19+)
SO_ATTACH_REUSEPORT_CBPF附加reuseport BPF与SO_REUSEPORT一起使用,设置经典BPF程序定义数据包如何分配给reuseport组中的套接字
SO_ATTACH_REUSEPORT_EBPF附加reuseport EBPF与SO_REUSEPORT一起使用,设置扩展BPF程序定义数据包如何分配给reuseport组中的套接字
SO_BINDTODEVICE绑定到设备将套接字绑定到特定设备如”eth0”
SO_BROADCAST广播标志允许数据报套接字向广播地址发送数据包
SO_BSDCOMPATBSD错误兼容性启用BSD错误兼容性(Linux 2.0和2.2中由UDP使用)
SO_DEBUG套接字调试启用套接字调试(需要CAP_NET_ADMIN能力或用户ID为0)
SO_DETACH_FILTER移除BPF过滤器移除通过SO_ATTACH_FILTER附加到套接字的BPF程序(Linux 2.2+)
SO_DETACH_BPF移除扩展BPF移除通过SO_ATTACH_BPF附加到套接字的扩展BPF程序(Linux 3.19+)
SO_DOMAIN套接字域检索套接字域作为整数,返回AF_INET6等值(只读,Linux 2.6.32+)
SO_ERROR套接字错误获取并清除挂起的套接字错误(只读)
SO_DONTROUTE不使用路由不通过网关发送,仅直接发送到连接的主机
SO_INCOMING_CPUCPU亲和性设置或获取套接字的CPU亲和性(Linux 3.19+获取,4.4+设置)
SO_INCOMING_NAPI_IDNAPI ID返回与接收最后一个数据包的RX队列相关联的NAPI ID(Linux 4.12+)
SO_KEEPALIVE保持连接在面向连接的套接字上启用保活消息的发送
SO_LINGER延迟关闭设置或获取SO_LINGER选项,参数是linger结构
SO_LOCK_FILTER锁定过滤器设置后防止更改与套接字关联的过滤器
SO_MARK数据包标记为通过此套接字发送的每个数据包设置标记(Linux 2.6.25+)
SO_OOBINLINE带外数据如果启用,带外数据将直接放入接收数据流中
SO_PASSCRED传递凭证启用或禁用接收SCM_CREDENTIALS控制消息
SO_PASSSEC传递安全上下文启用或禁用接收SCM_SECURITY控制消息
SO_PEEK_OFF窥视偏移设置recv(2)系统调用与MSG_PEEK标志一起使用时的”窥视偏移量”值(Linux 3.4+,仅unix套接字)
SO_PEERCRED对等进程凭证返回连接到此套接字的对等进程的凭证
SO_PEERSEC对等安全上下文返回连接到此套接字的对等套接字的安全上下文(Linux 2.6.2+)
SO_PRIORITY协议优先级为在此套接字上发送的所有数据包设置协议定义的优先级
SO_PROTOCOL套接字协议检索套接字协议作为整数,返回IPPROTO_SCTP等值(只读,Linux 2.6.32+)
SO_RCVBUF接收缓冲区大小设置或获取以字节为单位的最大套接字接收缓冲区
SO_RCVBUFFORCE强制接收缓冲区大小特权进程可以执行与SO_RCVBUF相同任务,但可以覆盖rmem_max限制(Linux 2.6.14+)
SO_RCVLOWAT接收低水位标记指定缓冲区中的最小字节数,直到套接字层将数据传递给用户
SO_RCVTIMEO接收超时指定接收超时,直到报告错误
SO_REUSEADDR地址重用指示在验证bind(2)调用中提供的地址时应允许重用本地地址
SO_REUSEPORT端口重用允许多个AF_INET或AF_INET6套接字绑定到相同的套接字地址(Linux 3.9+)
SO_RXQ_OVFL接收队列溢出指示应将无符号32位值辅助消息附加到接收的skbs,指示自创建套接字以来丢弃的数据包数(Linux 2.6.33+)
SO_SELECT_ERR_QUEUE错误队列选择当设置此选项时,套接字上的错误条件不仅会通过select(2)的exceptfds集合通知(Linux 3.10+)
SO_SNDBUF发送缓冲区大小设置或获取以字节为单位的最大套接字发送缓冲区
SO_SNDBUFFORCE强制发送缓冲区大小特权进程可以执行与SO_SNDBUF相同任务,但可以覆盖wmem_max限制(Linux 2.6.14+)
SO_SNDLOWAT发送低水位标记指定缓冲区中的最小字节数,直到套接字层将数据传递给协议
SO_SNDTIMEO发送超时指定发送超时,直到报告错误
SO_TIMESTAMP时间戳启用或禁用接收SO_TIMESTAMP控制消息
SO_TIMESTAMPNS纳秒时间戳启用或禁用接收SO_TIMESTAMPNS控制消息(Linux 2.6.22+)
SO_TYPE套接字类型获取套接字类型作为整数(如SOCK_STREAM)(只读)
SO_BUSY_POLL忙轮询设置在没有数据时忙轮询阻塞接收的近似时间(微秒)(Linux 3.11+)

全网/子网广播#

广播允许一个设备向网络中的所有其他设备同时发送数据。
在创建socket后,需要setsockop设置socket广播标志 server端需要设置全网地址255.255.255.255(或子网广播地址)。

proto.h

#pragma once
#define NAMESIZE 16
#define SERVER_PORT "1999"
#include "stdint.h"
struct msg_st {
uint8_t name[NAMESIZE];
uint32_t math;
uint32_t chinese;
} __attribute__((packed));

server.c

#include "proto.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "sys/socket.h"
#include "unistd.h"
#include <arpa/inet.h>
#define BUFFSIZE 1024
#define IPSIZE 24
#define BCAST_SERVER "255.255.255.255"
int main(int argc, char *argv[]) {
struct msg_st *sbuf_p;
sbuf_p = malloc(sizeof(struct msg_st));
if (sbuf_p == NULL) {
perror("malloc()");
exit(1);
}
sbuf_p->chinese = htonl(rand() % 100);
sbuf_p->math = htonl(rand() % 100);
strcpy(sbuf_p->name, "Jim");
int sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
perror("socket()");
exit(1);
}
int32_t opval = 1;
//设置全局广播
if (setsockopt(sd, SOL_SOCKET, SO_BROADCAST, &opval, sizeof(opval)) < 0) {
perror("setsockopt()");
exit(1);
}
struct sockaddr_in s_addr;
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(SERVER_PORT));
inet_pton(AF_INET, BCAST_SERVER, &s_addr.sin_addr);
socklen_t len;
len = sizeof(s_addr);
// bind(sd, (const struct sockaddr *)&s_addr, len);
while (1) {
sbuf_p->chinese = htonl(rand() % 100);
sbuf_p->math = htonl(rand() % 100);
if (sendto(sd, sbuf_p, sizeof(struct msg_st), 0,
(const struct sockaddr *)&s_addr, len) < 0) {
perror("sendto");
exit(1);
}
puts("OK");
sleep(1);
}
free(sbuf_p);
return 0;
}

client.c

#include "arpa/inet.h"
#include "proto.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "sys/socket.h"
int main(int argc, char *argv[]) {
struct msg_st msg;
int sd;
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
perror("socket()");
exit(1);
}
struct sockaddr_in laddr;
struct sockaddr_in saddr;
socklen_t saddr_len;
saddr_len = sizeof(saddr);
laddr.sin_family = AF_INET;
laddr.sin_port = htons(atoi(SERVER_PORT));
inet_pton(AF_INET, "0.0.0.0", &laddr.sin_addr);
socklen_t laddr_len;
laddr_len = sizeof(laddr);
if (bind(sd, (void *)&laddr, sizeof(laddr)) < 0) {
perror("bind()");
exit(1);
}
if (recvfrom(sd, &msg, sizeof(struct msg_st), 0, (struct sockaddr *)&saddr,
&saddr_len) < 0) {
perror("recvfrom()");
exit(1);
}
char ipstr[20];
inet_ntop(AF_INET, &saddr.sin_addr, ipstr, saddr_len);
printf("--MESSAGE FROM %s:%d--\n", ipstr, ntohs(saddr.sin_port));
printf("NAME=%s\n", msg.name);
printf("CHINESE=%d\n", ntohl(msg.chinese));
printf("MATH=%d\n", ntohl(msg.math));
return 0;
}

运行结果:

![[Pasted image 20250826115159.png]]

多播/组播#

组播(Multicast)允许一个或多个发送者(组播源)向多个接收者同时发送数据。

proto.h

#pragma once
#define NAMESIZE 16
#define SERVER_PORT "1999"
#define MTGROUP "224.2.2.2"
#include "stdint.h"
struct msg_st {
uint8_t name[NAMESIZE];
uint32_t math;
uint32_t chinese;
} __attribute__((packed));

server.c

#include "net/if.h"
#include "netinet/in.h"
#include "netinet/ip.h"
#include "proto.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "sys/socket.h"
#include "unistd.h"
#include <arpa/inet.h>
#define BUFFSIZE 1024
#define IPSIZE 24
int main(int argc, char *argv[]) {
struct msg_st *sbuf_p;
sbuf_p = malloc(sizeof(struct msg_st));
if (sbuf_p == NULL) {
perror("malloc()");
exit(1);
}
sbuf_p->chinese = htonl(rand() % 100);
sbuf_p->math = htonl(rand() % 100);
// sbuf_p->name = "Jim";
strcpy(sbuf_p->name, "Jim");
int sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
perror("socket()");
exit(1);
}
struct ip_mreqn mreq;
inet_pton(AF_INET, MTGROUP, &mreq.imr_multiaddr);
inet_pton(AF_INET, "0.0.0.0", &mreq.imr_address);
mreq.imr_ifindex = if_nametoindex("wlp0s20f3");
if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &mreq,
sizeof(struct ip_mreqn)) < 0) {
perror("setsockopt() IP_MULTICAST_IF");
exit(1);
}
struct sockaddr_in s_addr;
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(SERVER_PORT));
inet_pton(AF_INET, MTGROUP, &s_addr.sin_addr);
socklen_t len;
len = sizeof(s_addr);
// bind(sd, (const struct sockaddr *)&s_addr, len);
while (1) {
sbuf_p->chinese = htonl(rand() % 100);
sbuf_p->math = htonl(rand() % 100);
if (sendto(sd, sbuf_p, sizeof(struct msg_st), 0,
(const struct sockaddr *)&s_addr, len) < 0) {
perror("sendto");
exit(1);
}
puts("OK");
sleep(1);
}
free(sbuf_p);
return 0;
}

client.c

#include "arpa/inet.h"
#include "net/if.h"
#include "proto.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "sys/socket.h"
int main(int argc, char *argv[]) {
struct msg_st msg;
int sd;
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
perror("socket()");
exit(1);
}
struct ip_mreqn mreq;
inet_pton(AF_INET, MTGROUP, &mreq.imr_multiaddr);
inet_pton(AF_INET, "0.0.0.0", &mreq.imr_address);
mreq.imr_ifindex = if_nametoindex("wlp0s20f3");
if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
sizeof(struct ip_mreqn)) < 0) {
perror("setsockop()");
exit(1);
}
struct sockaddr_in laddr;
struct sockaddr_in saddr;
socklen_t saddr_len;
saddr_len = sizeof(saddr);
laddr.sin_family = AF_INET;
laddr.sin_port = htons(atoi(SERVER_PORT));
inet_pton(AF_INET, "0.0.0.0", &laddr.sin_addr);
socklen_t laddr_len;
laddr_len = sizeof(laddr);
if (bind(sd, (void *)&laddr, sizeof(laddr)) < 0) {
perror("bind()");
exit(1);
}
if (recvfrom(sd, &msg, sizeof(struct msg_st), 0, (struct sockaddr *)&saddr,
&saddr_len) < 0) {
perror("recvfrom()");
exit(1);
}
char ipstr[20];
inet_ntop(AF_INET, &saddr.sin_addr, ipstr, saddr_len);
printf("--MESSAGE FROM %s:%d--\n", ipstr, ntohs(saddr.sin_port));
printf("NAME=%s\n", msg.name);
printf("CHINESE=%d\n", ntohl(msg.chinese));
printf("MATH=%d\n", ntohl(msg.math));
return 0;
}

运行结果:

网络套接字:报式套接字&UDP【Unix编程】
https://milkfunc.top/posts/unix环境高级编程学习摘要/网络套接字/网络套接字报式套接字/
作者
CapCake
发布于
2025-08-21
许可协议
CC BY-NC-SA 4.0