UDP - C/S模型

news2025/1/22 16:04:33

        由于UDP不需要维护连接,程序逻辑简单了很多,但是UDP协议是不可靠的,保证通讯可靠性的机制需要在应用层实现。

通信函数

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

参数:

        sockfd: 套接字

        buf:缓冲区地址

        len:缓冲区大小

        flags: 0

        src_addr:(struct sockaddr *)&addr 传出。 对端地址结构

        addrlen:传入传出。

返回值:

        成功:接收数据字节数。

        失败:-1 errn。

        0: 对端关闭。 

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

参数: 

        sockfd: 套接字

        buf:存储数据的缓冲区

        len:数据长度

        flags: 0

        src_addr:(struct sockaddr *)&addr 传入。 目标地址结构

        addrlen:地址结构长度。

返回值:

        成功:写出数据字节数。

        失败:-1, errno    

UDP通信实现流程

/* 编程模型 */
Server							Client
创建套接字(socket)				创建套接字(socket)
准备地址(本机地址sockaddr_in)	准备地址(目标机地址sockaddr_in)
绑定(bind(sockfd+addr))			.....
接受请求(recvfrom)				发送请求(sendto)
响应请求(sendto)					接受请求(recvfrom)
关闭套接字(close)				关闭套接字(close)

服务器端代码实现

  • UDP服务器端算法的步骤描述

        ① 调用socket()函数创建服务器端无连接套接字。

        ② 调用bind()函数将套接字绑定到本机的一个可用的端点地址。

        ③ 调用recvfrom()函数从套接字接收来自远程客户端的数据并存入到缓冲区中,同时获得远程客户的套接字端点地址并保存。

        ④ 基于保存的远程客户的套接字端点地址,调用sendto()函数将缓冲区中的数据从套接字发送给该远程客户。

        ⑤ 与客户交互完毕,调用close()函数将套接字关闭,释放所占用的系统资源。

注意:

  1. 没有 accept,不需要建立建立连接;        
  2. 使用 recvfrom 代替 read,失败返回 -1,成功返回 - 从内核缓冲区读到的字节数        
  3. 使用 sendto 代替 write,失败返回 -1,成功返回 - 写到内核缓冲区的字节数
/*server.c*/
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ctype.h>

#define SERV_PORT 8000

int main(void)
{
    struct sockaddr_in serv_addr, clie_addr;
    socklen_t clie_addr_len;
    int sockfd;
    char buf[BUFSIZ];
    char str[INET_ADDRSTRLEN];
    int i, n;


    /* 打开一个网络通讯端口,分配一个文件描述符sockfd */
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    bzero(&serv_addr, sizeof(serv_addr)); //初始化为空
    serv_addr.sin_family = AF_INET; //地址采用IPv4地址
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //地址从主机字节顺序转换成网络字节顺序
    serv_addr.sin_port = htons(SERV_PORT); //端口号从主机字节顺序转换成网络字节顺序

    /* 将文件描述符sockfd和服务器地址绑定 */
    bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));

    printf("Accepting connections ...\n");
    while (1) {
         /* 接收client端传过来的的字符串,写入buf */
        clie_addr_len = sizeof(clie_addr);
        n = recvfrom(sockfd, buf, BUFSIZ,0, (struct sockaddr *)&clie_addr, &clie_addr_len);
        if (n == -1)
            perror("recvfrom error");
        
        /* 打印客户端IP及端口 */
        printf("received from %s at PORT %d\n",
                inet_ntop(AF_INET, &clie_addr.sin_addr, str, sizeof(str)),
                ntohs(clie_addr.sin_port));

        /* 小写转为大写 */        
        for (i = 0; i < n; i++)
            buf[i] = toupper(buf[i]);

        /* 把数据发送给客户端 */
        n = sendto(sockfd, buf, n, 0, (struct sockaddr *)&clie_addr, sizeof(clie_addr));
        if (n == -1)
            perror("sendto error");
    }

    close(sockfd);

    return 0;
}

客户端代码实现

  •  UDP客户端算法的步骤描述

        ① 调用socket()函数创建客户端无连接套接字。

        ② 找到期望与之通信的远程服务器ip地址和协议端口号;然后再调用sendto()函数将缓冲区中的数据从套接字发送给远程服务器。

        ③ 调用recvfrom()函数从套接字接收来自远程服务器端的数据并存入缓冲区中。

        ④ 与服务器交互完毕,调用close()函数将套接字关闭,释放所占用的系统资源。

注意:

  1. 没有 connect,不需要建立建立连接;        
  2. 使用 sendto 代替 write,失败返回 -1,成功返回 - 写到内核缓冲区的字节数        
  3. 使用 recvfrom 代替 read,失败返回 -1,成功返回 - 从内核缓冲区读到的字节数
/*client.c*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ctype.h>

#define SERV_PORT 8000

int main(int argc, char *argv[])
{
    struct sockaddr_in servaddr;
    int sockfd, n;
    char buf[BUFSIZ];

    /* 打开一个网络通讯端口,分配一个文件描述符sockfd */
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    bzero(&servaddr, sizeof(servaddr)); //初始化为空
    servaddr.sin_family = AF_INET; //地址采用IPv4地址
    inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr); //将“点分十进制” -> “二进制整数”
    servaddr.sin_port = htons(SERV_PORT); //端口号从主机字节顺序转换成网络字节顺序

    //bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    while (fgets(buf, BUFSIZ, stdin) != NULL) {
        /* 发送给服务端 */
        n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
        if (n == -1)
            perror("sendto error");
        
        /* 从服务端接收数据 */
        n = recvfrom(sockfd, buf, BUFSIZ, 0, NULL, 0);         //NULL:不关心对端信息
        if (n == -1)
            perror("recvfrom error");
        
        /* 输出服务器处理后的数据 */
        write(STDOUT_FILENO, buf, n);
    }

    close(sockfd);

    return 0;
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/434248.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

无人机动力系统优化测试平台-15kg级-Flight Stand 15

产品简介 通过Flight Stand 15测试台对电机和螺旋桨的拉力&#xff0c;扭矩&#xff0c;转速&#xff0c;电流&#xff0c;电压&#xff0c;温度&#xff0c;空速&#xff0c;螺旋桨效率和电机效率的测量帮助您精准地描述和评估其性能参数&#xff0c;这是我们五年多来的无人机…

[JAVASE]初识Java:数据类型与变量

CSDN的各位友友们你们好,今天千泽为大家带来的是 [JAVASE]初识Java&#xff1a;数据类型与变量、运算符, 接下来让我们一起了解一下吧! 如果对您有帮助的话希望能够得到您的支持和关注,我会持续更新 数据类型与变量 数据类型 在Java中数据类型分为基本数据类型与引用数据类…

15.2 矩阵链乘法

1.代码 public class MatrixChainMultiplication {public static void main(String[] args) { // 在该代码中&#xff0c;我们首先创建了两个n * n的矩阵m和s&#xff0c;分别用于记录最优值和分割点。 其中m 矩阵 通过i j 来显示在i到j的矩阵链中最优解 // // …

JavaSE/异常

博客制作不易&#xff0c;欢迎各位点赞&#x1f44d;收藏⭐关注 前言 在使用Java编写代码时&#xff0c;我们难免会遇到数组越界、运行超时、栈溢出等异常问题。所以如果我们熟练掌握异常的定义和使用&#xff0c;这对我们学习Java有很大的帮助。 一、异常的定义 程序执行过程…

图书馆管理系统【GUI/Swing+MySQL】(Java课设)

系统类型 Swing窗口类型Mysql数据库存储数据 使用范围 适合作为Java课设&#xff01;&#xff01;&#xff01; 部署环境 jdk1.8Mysql8.0Idea或eclipsejdbc 运行效果 本系统源码地址&#xff1a;https://download.csdn.net/download/qq_50954361/87682509 更多系统资源库…

计算机视觉的热门研究方向与发展趋势

计算机视觉产业链 工业界&#xff1a;对学术研究提出需求 最火的两个概念&#xff1a;自动驾驶和元宇宙 相关热点研究方向&#xff1a; &#xff08;1&#xff09;建图技术&#xff1a;三维重建技术&#xff0c;包括SLAM、定位、建图、更新等技术&#xff1b;&#xff08;2&…

学习机器人SLAM导航核心技术(一)

本章节&#xff0c;我们将学习基于SLAM导航的机器人技术。 学习资料主要参照张虎的《机器人SLAM导航核心技术与实战》和高翔的《视觉SLAM十四讲》。 以机器人SLAM导航核心技术与实战作为学习的技术路线。在此基础上增加自己学习到的内容。 第1章&#xff1a;ROS入门必备知识 …

Git介绍与相关操作

git简介 git是用于版本控制的软件&#xff0c;保留历史记录&#xff0c;便于回溯 GitHub中文官方文档 访问GitHub GitHub520 无法访问GitHub太常见了&#xff0c;主要方法就是查ip&#xff0c;改本地host文件。 GitHub520本质也是修改hosts来实现&#xff0c;但是无需手动查…

Pycharm Debug调试技巧

Pycharm Debug调试 Python中的Bug是指程序在执行过程中出现错误的情况。这些错误可能会导致程序崩溃、输出意外的结果或者根本不运行。 常见的Python Bug包括&#xff1a; 语法错误&#xff1a;例如拼写错误、缺少冒号、使用了错误的缩进等等。逻辑错误&#xff1a;程序可以…

草图大师su通过照片建模建筑 零插件sketchup

打开sketchup&#xff0c;相机-匹配新照片。照片的要求&#xff1a; “匹配照片”最适合主要由直角组成的结构。您肯定至少需要一个直角才能使用匹配照片。使用从角落以大约 45 度角拍摄的照片。上图来自谷歌街景图是以 45 度角拍摄的图像示例。如果您正在拍照&#xff0c;则可…

模糊数学求传递闭包(C语言)

一、问题描述 我们在模糊数学中利用传递闭包法进行聚类分析的时候&#xff0c;需要求模糊相似矩阵的传递闭包&#xff0c;对于阶数比较高的矩阵&#xff0c;手工计算太过于繁琐&#xff0c;我们可以借助程序实现。 我们先来看一下模糊矩阵的合成规则&#xff1a; 设为有限论域…

ChatGPT能取代传统伪原创工具吗?

文章伪原创工具是一种计算机辅助写作工具&#xff0c;通过算法和技术实现对原有文章内容进行修改和改写&#xff0c;生成新的、看起来不同于原文的文章&#xff0c;以达到避免抄袭和提高原创性的目的。今天我们来聊聊传统伪原创工具和使用ChatGPT进行文章改写的优缺点。 一、传…

【状态估计】电力系统状态估计的虚假数据注入攻击建模与对策(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

C++ Linux Web Server 面试基础篇-操作系统(一、Linux基础)

⭐️我叫忆_恒心&#xff0c;一名喜欢书写博客的在读研究生&#x1f468;‍&#x1f393;。 如果觉得本文能帮到您&#xff0c;麻烦点个赞&#x1f44d;呗&#xff01; 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧&#xff0c;喜欢的小伙伴给个三…

SpringBoot访问静态资源和jar外部静态资源,部署前端打包后的vue项目放入静态资源里

记录一下使用SpringBoot访问静态资源和SpringBoot打包之后的jar外部静态资源&#xff0c;在开发的时候&#xff0c;一般选择前后端分离的方式&#xff0c;前端使用vue 后端使用SpringBoot&#xff0c;通常情况下&#xff0c;部署都是前端通过http去请求后端资源&#xff0c;涉及…

CVE-2021-27905 Apache Solr SSRF漏洞

预备知识 1. 漏洞信息 漏洞编号&#xff1a;CVE-2021-27905 漏洞名称&#xff1a;Apache Solr SSRF漏洞 漏洞描述&#xff1a;Apache Solr是一个开源的搜索服务&#xff0c;使用Java编写、运行在Servlet容器的一个独立的全文搜索服务器&#xff0c;是Apache Lucene项目的开源…

在Claude穿越千年,问了孔子人生问题

&#x1f4d5;如果大家还不知道Claude的话&#xff0c;可以先了解下&#xff0c;它是chatgpt最大的竞争对手。他有很多的知识&#xff0c;能做的事情也非常的多。 今天我们就让他用孔子的思想来跟我们对话&#xff0c;看看他会怎么回答我们的人生问题。 我们先问问他&#xf…

组播协议的RP

某组播网络示意图如图,其中MCS1是组播地址(G1)的组播源,PC1和PC2是组G1的接收者。 1.上述组播网络中,端口1、2、3需要运行哪些协议,为什么需要运行这些协议?(仅填必配的组播协议)详细写出具体原因 结合上述拓扑,考虑到存在RP场景,因此运行组播路由协议为PIM-SM协议,…

『pyqt5 从0基础开始项目实战』07. 次数清零与多行数据删除(保姆级图文)

目录 导包和框架代码 次数清零按钮添加绑定事件实现数据清零 数据删除按钮添加绑定事件实现删除功能完整代码总结 欢迎关注 『pyqt5 从0基础开始项目实战』 专栏&#xff0c;持续更新中 欢迎关注 『pyqt5 从0基础开始项目实战』 专栏&#xff0c;持续更新中 导包和框架代码 请…

GEE:Gmeans图像分割

G-means是一种聚类算法,它是基于K-means算法的改进版本。K-means算法的一个主要缺点是需要事先指定聚类的数量,而G-means算法则可以自动确定聚类的数量。 G-means算法使用了类似于K-means的迭代过程,但在每次迭代时,它会检查每个聚类是否可以继续细分为两个子聚类。这个检…