当前位置: 首页 > news >正文

Linux(socket网络编程)UDP---初学

提要

UDP的缺点:
(1)丢得少不找回,可以设置阈值
(2)数据段顺序不能保障
UDP的优点:
没有繁琐的连接过程

TCP和UDP:
(1)TCP通常比UDP慢点:
1.TCP收发数据前后要进行连接和关闭清理。
2.收发数据量越小,TCP效率就越低。
(2)TCP能保障数据的完整性。
(3)TCP中套接字是一对一关系,服务端为了和多个客户端通信,要建立多个用于通信的套接字
(4)UDP中,客户端和服务端都只需要一个套接字

实验

UDP通信的程序中
先 完成对套接字的地址分配工作
然后 调用sendto传输数据,调用sendto函数时自动分配IP和端口号

代码步骤

服务端:
1.创建套接字描述符、udp套接字;
2.创建并设置地址结构体,用bind函数把结构体绑定到套接字;
3.recvfrom函数用于接收;sendto函数用于发送

客户端:
1.创建套接字描述符、创建UDP套接字;
2.创建地址结构体,结构体要设置服务器的IP和端口号。
3.sendto用于发送;recvfrom用于接收

sendto函数和recvfrom函数
#include <sys/types.h>
#include <sys/socket.h>

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

recvfrom()
sockfd:套接字文件描述符
buf:指向存储接收数据的缓冲区的指针
len:缓冲区的大小
flags:通常设置为0
src_addr:指向存储发送方地址的sockaddr结构的指针
addrlen:指向存储src_addr长度的变量的指针
返回值:成功时返回接收到的字节数,失败返回-1。

flags
MSG_PEEK:查看数据但不从队列中移除。
MSG_WAITALL:等待接收完整的消息(仅用于TCP)。
MSG_DONTWAIT:非阻塞模式。

sendto
sockfd:套接字文件描述符
buf:指向包含要发送数据的缓冲区的指针
len:要发送的数据的长度
flags:通常设置为0
dest_addr:指向包含目标地址的sockaddr结构的指针
addrlen:dest_addr的长度
返回值:成功时返回发送的字节数,失败返回-1

flags
MSG_CONFIRM:请求对方确认数据已接收
MSG_DONTROUTE:绕过路由表,直接发送数据
MSG_DONTWAIT:非阻塞模式

代码示例
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h> //struct sockaddr_in,htons(),htonl(),INADDR_ANY
#include <stdlib.h>//atoi()
#include <sys/wait.h>//close()
#include <arpa/inet.h>//inet_addr()
int udp_server(int argc, char* argv[]) {
    
    int ser_sock = -1;//服务器套接字描述符
    char message[512] = "";
    struct sockaddr_in servaddr {};//服务器地址结构体
    
    //检查命令行参数数量
    if (argc != 2) {
        printf("usage:%d <port>\n", argv[0]);
        printf("error:argement is error\n");
        return -1;
    }

    //创建udp套接字
    ser_sock = socket(PF_INET, SOCK_DGRAM, 0);
    if (ser_sock == -1) {
        printf("error:create socket failed\n");
        return -1;
    }

    //设置服务器结构体
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//监听所有可用端口
    servaddr.sin_port = htons((short)atoi(argv[1]));//转换端口号为网络字节序

    //绑定套接字到指定地址和端口
    if (bind(ser_sock, (struct sockaddr*) & servaddr, sizeof(servaddr)) == -1) {
        printf("error:bind failed");
        close(ser_sock);
        return -1;
    }

    struct sockaddr_in clientaddr;//
    socklen_t clientlen{};
    //循环接收并回显消息10次
    for (int i = 0; i < 10; i++) {
        clientlen = sizeof(clientaddr);
        ssize_t len = recvfrom(ser_sock, message, sizeof(message), 0, (struct sockaddr*)&clientaddr, &clientlen);
        sendto(ser_sock, message, len, 0, (struct sockaddr*)&clientaddr, clientlen);
    }

    close(ser_sock);
    return 0;
}
#include<string.h>//strcmp()
int udp_client(int argc, char* argv[])
{
    int client_sock;//客户端套接字描述符
    struct sockaddr_in serv_addr;//地址结构体,存的是服务端的地址
    socklen_t serv_len = sizeof(serv_addr);
    char message[512] = "";

    if (argc != 3) {
        printf("usage:%s ip port\n", argv[0]);
        printf("error:argement error!");
        return -1;
    }

    //创建UDP套接字
    client_sock = socket(PF_INET, SOCK_DGRAM, 0);
    if (client_sock == -1) {
        printf("error:socket create failed!");
        return -1;
    }

    //设置地址结构体
    serv_addr.sin_family = AF_INET;//地址族IPv4
    serv_addr.sin_addr.s_addr = inet_addr(argv[1]);//服务器IP地址
    serv_addr.sin_port = htons((short)atoi(argv[2]));//服务器端口号

    //循环发送和接收消息,直到用户输入‘q’或‘Q’
    while (1) {
        printf("input message(q to Quit):");
        scanf("%s", message);
        if ((strcmp(message, "q") == 0) || (strcmp(message, "Q") == 0)) {
            break;
        }
        ssize_t len = sendto(client_sock, message, strlen(message), 0, (sockaddr*)&serv_addr, serv_len);
        memset(message, 0, sizeof(message));
        recvfrom(client_sock, message, sizeof(message), 0, (sockaddr*)&serv_addr, &serv_len);
        printf("recv:%s\n", message);
    }

    close(client_sock);
    return 0;
}

void test(char* argv0)
{
    if (fork() > 0) {
        int argc = 3;
        char* argv[] = {
            argv0,
            (char*)"127.0.0.1",//const char[]类型转换为char*类型,避免被警告
            (char*)"9999"
        };
        udp_client(argc, argv);
        int status = 0;
        wait(&status);//等待子进程结束
    }
    else {
        int argc = 2;
        char* argv[] = {
            argv0,
            (char*)"9999"
        };
        udp_server(argc,argv);
    }
}

int main(int argc,char* argv[])
{
    test(argv[0]);
    return 0;
}

在这里插入图片描述

http://www.dtcms.com/a/20691.html

相关文章:

  • nacos学习笔记
  • DeepSeek-R1:通过强化学习激励大型语言模型的推理能力
  • 【LeetCode】3.无重复字符的最长字串
  • Qt中基于开源库QRencode生成二维码(附工程源码链接)
  • Mac 开发工具推荐
  • 《云原生安全攻防》-- K8s镜像安全:镜像全生命周期安全管理
  • 深入解析类方法与静态方法 —— 内存分析与直观图示详解
  • 项目访问出现504 Gateway Time-out nginx/1.21.6的情况
  • 注册表单提交加验证码功能
  • 服务器租用:虚拟化技术都包含哪些内容?
  • 2025年2月9日(数据分析_2)
  • Spring AI发布!让Java紧跟AI赛道!
  • ailx10的发明专利思路
  • HDFS体系结构
  • 【深度学习】深度学习和强化学习算法——深度 Q 网络DQN
  • 【SQL SERVER】sqlserver 该表字段如果被用作其他表的外键不能被truncate table
  • 掌握xtquant实时行情订阅:量化交易的关键一步
  • 强化学习笔记7——DDPG到TD3
  • 速度与激情:4.5吨轻卡阻力与刹车力模型的终极拆解——从仿真台架到真实路况的硬核对话
  • 2025年2月16日(numpy-deepseek)
  • 【云原生】SpringCloud-Spring Boot Starter使用测试
  • Spring源码分析のBean创建流程(上)
  • OpenCV简介
  • 用命令模式设计一个JSBridge用于JavaScript与Android交互通信
  • Python —— format函数的使用
  • Copilot in OneNote(WebTeams)功能提升效率加倍
  • open3d绘制平面
  • kamailio的伪变量
  • 2.2 反向传播:神经网络如何“学习“?
  • 从Vec3实现复习运算符重载