linux udp socket
服务端源码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define BUFF_SIZE 1024
int main()
{
int sock = 0;
int recvlen = 0;
// 接收数据缓冲区
char buff[BUFF_SIZE] = {0};
struct sockaddr_in addr;
// 初始化地址结构体
memset(&addr, 0, sizeof(addr));
// 设置地址协议簇为AF_INET,也就是ipv4
addr.sin_family = AF_INET;
// 设置监听端口
addr.sin_port = htons(12345);
socklen_t addrlen;
// 创建socket,AF_INET表示ipv4,SOCK_DGRAM表示udp
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
perror("socket error");
exit(-1);
}
// 把sock绑定到指定的端口上
if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1)
{
perror("bind error");
exit(-1);
}
struct sockaddr_in client_addr;
while (1)
{
memset(buff, 0, BUFF_SIZE);
// 接收数据
// sock是需要接收的socket
// buff用来保存接收的数据
// 1024表示buff有多大,最多只能接收这么多
// client_addr用来保存发送数据段的地址信息
// addrlen表示地址信息的长度,因为ipv4和ipv6地址长度不一致,所以需要指明有多长
// recvlen表示接收了多少字节的数据
recvlen = recvfrom(sock, buff, BUFF_SIZE, 0, (struct sockaddr *) &client_addr, &addrlen);
if (recvlen > 0)
{
printf("%s\n", buff);
// 把数据发送出去
// 发送还是通过sock
// buff是发送的数据
// recvlen是发送的数据长度
// client_addr是需要发送的ip地址和端口信息,这里用上面接收到的地址,就相当于再发送回去
// addrlen用来描述client_addr的大小
sendto(sock, buff, recvlen, 0, (struct sockaddr *) &client_addr, addrlen);
}
}
}
客户端源码
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/ip.h>
#include <linux/udp.h>
#define BUFF_SIZE 1024
int main(int argc, char *argv[])
{
int sock_r;
struct sockaddr_in svr_addr;
int ret;
socklen_t addrlen = sizeof(struct sockaddr_in);
char buf[BUFF_SIZE] = {};
// 创建socket
if ((sock_r = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("socket error");
exit(-1);
}
svr_addr.sin_family = AF_INET;
svr_addr.sin_port = htons(12345);
svr_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
while (1)
{
memset(buf, 0, BUFF_SIZE);
printf("input: ");
fgets(buf, BUFF_SIZE, stdin);
// 向svr_addr指定的地址发送数据
sendto(sock_r, buf, BUFF_SIZE, 0, (struct sockaddr *) &svr_addr, addrlen);
// 从sock_r接收数据
ret = recvfrom(sock_r, buf, BUFF_SIZE, 0, (struct sockaddr *) &svr_addr, &addrlen);
printf("%s\n", buf);
}
close(sock_r);
return 0;
}
我们可以看到服务端使用了bind
函数,而客户端确可以直接发送数据,这是为什么呢?因为socket创建好之后,系统也不知道你需要监听哪个端口。服务端是被动接收数据,必须bind后,系统才知道这个socket监听的哪个端口,不然recvfrom
就不知道从哪里获取数据。
而客户端是先发送数据,发送数据时系统会随机分配一个端口给客户端的socket使用,也就是底层帮我们做了绑定。
send和sendto
https://man7.org/linux/man-pages/man2/send.2.html
#include <sys/socket.h>
ssize_t send(int sockfd, const void buf[.len], size_t len, int flags);
ssize_t sendto(int sockfd, const void buf[.len], size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
linux发送数据有多个api,除了上面的这些,还有一个write
。在官方文档中介绍的很清楚,send
是用在connect
之后,因为send
没有指定发送的地址,必须先使用connect
为socket确定发送端地址信息。
由于sendto
指定了地址信息,如果使用connect
后,再使用sendto
,并且dest_addr
不为NULL
,地址信息与connect
的不匹配,就会报错,因为系统不知道你到底要发送到哪个地址信息。
write
大部分用作文件操作,不过在linux下一切皆文件,所以也可以操作socket,与send
功能相同,只是少了flags
参数。
sendmsg
会发送一个msghdr
结构体,可以对发送的socket做更多控制。
客户端源码
使用send
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/ip.h>
#include <linux/udp.h>
#define BUFF_SIZE 1024
int main(int argc, char *argv[])
{
int sock_r;
struct sockaddr_in svr_addr;
int ret;
socklen_t addrlen = sizeof(struct sockaddr_in);
char buf[BUFF_SIZE] = {};
// 创建socket
if ((sock_r = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("socket error");
exit(-1);
}
svr_addr.sin_family = AF_INET;
svr_addr.sin_port = htons(12345);
svr_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// 增加了connect的操作,把svr_addr描述的地址信息与sock_r绑定
connect(sock_r, (struct sockaddr*)&svr_addr, sizeof(svr_addr));
while (1)
{
memset(buf, 0, BUFF_SIZE);
printf("input: ");
fgets(buf, BUFF_SIZE, stdin);
// 向svr_addr指定的地址发送数据
//sendto(sock_r, buf, BUFF_SIZE, 0, (struct sockaddr *) &svr_addr, addrlen);
// 直接使用send发送数据,不需指定地址信息
send(sock_r, buf, BUFF_SIZE, 0);
// 从sock_r接收数据
ret = recvfrom(sock_r, buf, BUFF_SIZE, 0, (struct sockaddr *) &svr_addr, &addrlen);
printf("%s\n", buf);
}
close(sock_r);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏