socket编程---fgets和fputs函数使用理解

2023-05-16

这一节是继续上一节socket05的讨论,来探讨在使用socket进行通信中遇到的一些函数使用理解误区

1.fgets的使用注意点

在写socket通信(代码见上一篇中,只是将sendbuf和recvbuf的容量改变了其余不变)的时候遇到的问题,这个现象是:

在fgets(sendbuf,sizeof(sendbuf),stdin)的时候,假设我的sendbuf容量是n,也就是char sendbuf[n]

1.当我的输入小于n-1时,并且以换行为结尾的时候,fgets会在最后那个字符的后面加上一个\n字符,最后还要补上一个'\0',
然后发送过去

2.但是当我输入的字符串达到了n-1或大于n-1的时候,会截断到n-1处,然后在最后面加上一个\0字符(作为字符串结束)

更形象地说明这个现象,做一个假设,设定n=10,也就是buf均为10,之后再来进行通信

//在client.c中修改这两行
char sendbuf[10] = {0};
char recvbuf[10] = {0};
//在server.c中修改这一行
char recvbuf[10] = {0};

之后编译运行,通信情况如下图所示:

这里写图片描述

可以看出来,我在发送小于9的字符数时,都是正常进行通信的,但是第一个9个字符的helloworl就让程序卡死了,之后在client端再输入什么,server端都没再答应了。

这里我们先记住这个现象,接下来我们来了解一下fgets和fputs函数的用法,再联合程序来分析到底是代码的哪个步骤出了bug。

查了一下具体fgets和fputs的用法,稍微找到了一些能解释的说法:

fgets( ):会自动在字符数组最后加上一个'\0'(字符串以\0为识别,也是结束),如果字符数组长度不够
(没到达缓冲区的限制值),则截断前面部分,没有'\0',如果行尾有换行符(我们这里发送数据以换行符为
标识一行发送),换行符也被赋值进入数组,在'\0'的前面。

也就是说,无论什么时候,fgets都会在后面加上一个’\0’,只不过阀值比较特殊,甚至在我们这个程序中引发了一些错误。


当我输入:   hello\n
sendbuf中:   hello\n\0           这时候的有效字符为6个,除去\0无效


当达到阀值:  helloworl\n         换行符是第10位
sendbuf中:   helloworl\0         这时候因为末尾一位必须是\0,所以截断,换行符也被截断了,不算入有效字符

所以使用fgets时,只能录入有效数据9位最多,并且需要注意的是在这里的代码中(socket通信),由于我们自己封装的一个readline函数规定,client发送数据时需要将一个\n发送给对等段,才能使得server端的readn函数真正把数据读取出去,而不是卡死在缓冲区。顺而执行到下面的代码,包含了writen,回射client端。

2.server端和client端各个函数之间通信的顺序流程图:

这里写图片描述
结合图中,我们可以知道,为什么会出现如上的结果,是阻塞在了server端的readline函数中。
把源码拿过来看一下:

ssize_t readline(int sockfd,void *buf,size_t maxline)
{
    int ret;
    int nread;
    char* bufp = buf;   //指针指向buf缓冲区
    int nleft = maxline;
    while(1)
    {
        ret = recv_peek(sockfd,bufp,nleft);
        if(ret < 0)
            return ret;
        else if(ret == 0)
            return ret;      
        nread = ret;
        int i;
        for(i = 0;i<nread;i++)
        {
            if(bufp[i] == '\n')   //对等端必须将\n发送过来,不发送过来会导致程序卡死在这里
            {
                ret = readn(sockfd,bufp,i+1);
                if(ret != i+1)
                    exit(EXIT_FAILURE);
                return ret;
            }
        }
        if(nread > nleft)
            exit(EXIT_FAILURE);
        nleft -= nread;
        ret = readn(sockfd,bufp,nread);  
        if(ret != nread)
            exit(EXIT_FAILURE);

        bufp += nread;
    }
    return -1;
}

1.从代码中看,我们封装好一个sendbuf,假设client端是helloworl\n输入,这时候的helloworl\n被裁剪成了helloworl\0,这里上面详细解释过的
2.那么这样一个字符串作为sendbuf传递过来了,server进入readline函数,看以上代码,调用完了recv_peek,直接进入for循环
3.可以看出来,这个字符串是没有’\n’的,所以跳出for循环,进入下面代码,可是下面的代码一般是不执行的,因为正常遇到’\n’就直接返回退出函数。那么我们就是卡死在了这里。

得出一个结论:我们可以输入的有效字符数是容量capacity-2,留两位给\n和\0.

3.fputs函数

这个函数没对我们的程序造成什么大的影响,但是总是和fgets配对出现,将字符串打印到标准输出(屏幕…..)

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
int main()
{
        char ch[10] = "hello\n";
        printf("strlen = %d\n",(int)strlen(ch));
        printf("sizeof = %d\n",(int)sizeof(ch));

        char get[10];
        int count = 0;
        fputs("hello,this is fputs\n",stdout);
        while(1)
        {
                fgets(get,sizeof(get),stdin);
                for(int i = 0;i < strlen(get);i++)
                {
                        printf("%c",get[i]);
                        count = i;
                }
                printf("\ncount = %d\n",count);
                printf("strlen(get) = %d\n",(int)strlen(get));
        }

        return 0;
}

大家可以把这段代码粘过去编译执行一下,是输入一段字符,求字符的有效字符数。
这里写图片描述

fgets和fputs都在里面,fputs显得相对简单地多了,只是将一段字符打印到标准输出,fgets会把’\n’作为有效字符存入字符串中。

4.总结

我们找到了问题的所在,是因为fgets对字符串的切割使得传输有效字符串有了限制,这里我还也没能找到一个好的办法来解决这个问题,但是好在只要我们的sendbuf和recvbuf足够大,一般的信息传送不会使得程序崩溃。

如果遇到数据量比较大的时候,我们应该对边界作一个切割,把大块数据切割成多个小块,在后面加上’\n’和’\0’两个预留字符。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

socket编程---fgets和fputs函数使用理解 的相关文章

  • C语言中return的各种用法

    转自 xff1a https www pinlue com article 2021 06 2614 1611638969328 html
  • 从零开始搭二维激光SLAM --- 激光雷达数据效果对比

    我们知道 xff0c 不同品牌的激光雷达产生的数据是不一样的 xff0c 那这些不同点是如何影响建图效果的呢 xff1f 这篇文章就是来分析这个问题 xff0c 将从不同光强下的点云效果 xff0c 不同夹角下的点云效果 xff0c 以及
  • 海上垂直无人机垂直起降平台

    无人机的降落要实现对飞行轨迹和飞行状态的精确控制 xff0c 对目标地点的精确定位与识别 xff0c 而海上无人机降落具有更多不确定因素 xff0c 现提出一些有效方案 xff0c 以使得无人机海上起降成为可能 无人机起降条件 xff1a
  • 开源自制6通道航模遥控器,Arduino Pro Mini NRF24L01模块

    前言 前段时间跟着LOLI大神的教程制作了LOLI三代控 xff0c 效果很好 但是 xff0c 由于LOLI三代控的接收机带有数据回传功能 xff0c 也就是接收机的无线模块也承担了发射数据功能 xff0c 所以接收机也要使用带有功率放大
  • linux下TCP/IP通讯实例

    下面是server端源码 xff1a include lt stdio h gt include lt stdlib h gt include lt string h gt include lt unistd h gt include lt
  • STM32 USART 一些问题

    SECTION 1 1 2 3 4 5 6 7 8 9 10 11 12 13
  • 数据缓冲策略 —— 无缓冲、行缓冲、全缓冲(缓冲区大小测试)

    printf打印数据时 xff0c 一般会先把数据放入C缓冲区 xff0c 然后再刷新到内核缓冲区 xff0c 最后再写入硬件 这个过程中 xff0c 数据从C缓冲区迁移到内核缓冲区的操作我们称为缓冲 xff08 也可以理解为刷新 xff0
  • K210 FreeRTOS多任务多核系统调度

    一 目的 众所周知 xff0c K210这款AI新品是一款64bit 双核芯片 xff0c 其支持裸机编程 xff0c 并且官方也提供freertos sdk xff0c 方便开发者在其上进行多任务应用开发 那么如何进行任务创建和多核开发呢
  • keil如何添加h文件_KEIL 那些编辑技巧与方法

    当然了 xff0c 很多人现在更多的是使用 VSCode 或者 SI 等软件进行编辑 xff0c 但不可否认的是 xff0c 还有很多道友还是选择 KEIL 作为编辑软件的 xff0c 本篇笔记作为一个编辑技巧的总结 关于 KEIL 软件的
  • 关于keil C51 案例 加入头文件

    1 先在工程下面建立一个 h文件 xff0c 例如delay h 在其中写入要加入的函数声明 xff0c 或者其他的一些预定义 xff1a ifndef DELAY H define DELAY H include lt reg52 h g
  • "extern C", 你真的懂了吗?

    在c 43 43 prime书中看到过 xff0c 在DLL和lib中看到过 xff0c 但是每次看过就不求甚解地一扫而过 心里知道有extern c这个语句 xff0c 却不知道该用在哪里 xff0c 又能起到什么作用 唉 xff0c 想
  • 内存或寄存器定义和赋值

    在嵌入式中 xff0c 会经常遇到寄存器 内存的数据传输 xff0c 如何向寄存器中写入数据呢 xff1f 现举例说明 xff1a define rDISRC0 volatile unsigned 0x4b000000 DMA 0 Init
  • Makefile的文件格式,详解规则

    构建规则都写在Makefile文件里面 xff0c 要学会如何Make命令 xff0c 就必须学会如何编写Makefile文件 1 概述 Makefile文件由一系列规则 xff08 rules xff09 构成 每条规则的形式如下 xff
  • 只声明而不定义变量

    如果想声明一个变量而不定义它 xff0c 就在变量前面添加关键字extern xff0c 而且不要显式地初始化变量 extern int i 声明i而非定义i extern int j 61 0 定义 int k 声明并且定义 注意 xff
  • vector 与 智能指针使用注意事项

    看以下代码 xff0c 执行时会有什么问题 xff1f include lt vector gt include lt stdio h gt include lt stdlib h gt include lt memory gt class
  • SystemV 共享内存(一)—— 共享内存的创建与释放(shmget / shmctl)

    匿名管道和命名管道都是基于文件 的进程间通信 xff0c SystemV方案是在OS内核层面 专门为进程间通信设计的一个方案 xff0c 然后通过系统调用 xff08 system call xff09 给用户提供通信接口 SystemV方
  • TTL 485 232 全双工 半双工 单工---精简总结

    全双工 xff1a 双向2车道 半双工 xff1a 双向1车道 单工 xff1a 单向车道 1 从单片机软件编程角度来说 xff0c RS232 RS485都是转换为TTL电平方式与单片机通信 xff08 CAN收发器把差分信号转化为TTL
  • STM32F4-ADC-常规通道-转换模式配置-总结

    STM32F4 ADC常规通道转换的模式配置 模式选择 此寄存器定义 xff1a 是否自动循环 这两个寄存器定义 xff1a Scan mode xff08 是否轮询序列 xff09 和Discontinuous mode xff08 是否
  • 单片机 裸机 架构

    以前是 while xff08 1 xff09 43 软件定时器 43 中断标志 的框架 xff0c 现在的项目我想尝试一下新的框架 xff0c 简单来说是 主状态机 43 大量子状态机 43 软件定时器 的方式 xff0c 这其中状态机和
  • USART---串口发送数据

    xfeff xfeff while USART1 gt SR amp 0X40 61 61 0 等待发送结束 解析 xff1a USART1 gt SR xff1a 串口 状态 寄存器 USART1 gt SR amp 0X40 即串口状态

随机推荐

  • STM32---串口初始化

    u8 USART RX BUF USART REC LEN 接收缓冲 最大USART REC LEN个字节 bit15 xff0c 接收完成标志 bit14 xff0c 接收到0x0d bit13 0 xff0c 接收到的有效字节数目 u1
  • stm32---RS485初始化

    u8 RS485 RX BUF 64 接收缓冲 最大64个字节 u8 RS485 RX CNT 61 0 接收到的数据长度 函数 xff1a RS485 Init 功能 xff1a 串口初始化配置 参数 xff1a Baud 波特率 备注
  • 定时器0,中断,控制LED闪烁(1s亮,1s灭)---2018-11-07

    include lt reg52 h gt include lt stdio h gt define uchar unsigned char define uint unsigned int sbit LED 61 P2 2 void ti
  • AM2322温湿度传感器(地址0XB8)---I2C总结(I2C_ModBus协议)

  • 数码管---共阳---共阴---段选码---位选码---总结

    共阴极 xff1a 位选为低电平 xff08 即0 xff09 选中数码管 各段选为高电平 xff08 即1接 43 5V时 xff09 选中各数码段 0 f 共阴数码管段选 表 xff0c 无小数点 xff1a unsigned char
  • ubuntu怎样通过终端打开firefox?

    1 直接输入firefox 按回车 2 首先打开火狐浏览器 xff0c 鼠标移到屏幕最顶端 xff0c 出现菜单栏 工具栏 xff0d xff0d 附加组件选项 如下图所示 也可以在火狐浏览器界面 使用快捷键 shift 43 Ctrl 4
  • 重新认识 IP地址

    目录 一 什么是网段划分 二 如何分配子网中的IP xff1f 三 IP地址的分类 1 早期划分方式 1 早期分类方式 2 早期分类的局限性 2 CIDR划分 xff08 子网掩码划分 xff09 1 基本思路 2 实现方式 四 IP地址的
  • Linux服务器下抓包工具tcpdump分析

    概述 说到抓包分析 xff0c 最简单的办法莫过于在客户端直接安装一个Wireshark或者Fiddler了 xff0c 但是有时候由于接口调用无法在客户端抓包 xff0c 只能在服务器上抓包 xff0c 这种情况下怎么办呢 xff1f 本
  • MATLAB 常用转义字符

    MATLAB常用转义字符收录如下 Single quotation mark nbsp Percent character nbsp Backslash nbsp a Alarm nbsp b Backspace nbsp f Form f
  • 利用MATLAB解决人工神经网络模拟预测问题研究

    利用MATLAB解决人工神经网络模拟预测问题研究 nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp 人工神经网络根据模仿人脑的工作原理抽象出来的一种算法 人工神经网络 artigicial neutral ne
  • Visual Studio 2008学习过程(之一)起步

    以前 xff0c 在使用MATLAB开发一些软件 xff0c 虽然它的数值计算方面的功能很强大 xff0c 但是界面不是很好看 xff0c 很难做出来漂亮的软件 xff0c 所以萌生了用VS和MATLAB联合编程的想法 这样可以使软件更加强
  • 如何用servlet写网页访问量计数器?

    如何用servlet写网页访问量计数器 xff1f 1 原料 l MyEclipse l Tomcat l html 2 步骤 1 新建工程 项目栏鼠标右键 New Web Project xff0c 这里我起名为 xff1a myexm4
  • 提示:请安装TCP/IP协议.error=10106。解决方案

    有朋友使用电脑的时候会出现如下错误 xff0c 如何解决该问题是本文写作的目的 提示错误 xff1a 图 1 解决 方案 xff1a 1 删除两个注册表选项 xff1b 按下windows键 43 R键 xff0c 输入regedit xf
  • 防止头文件被重复包含

    前言 为了避免同一个文件被include多次 xff0c C C 43 43 中有两种方式 xff0c 一种是 ifndef方式 xff0c 一种是 pragma once方式 方式一 xff1a ifndef SOMEFILE H 或写为
  • 有趣的网站分享——pornhub风格生成器

    寄语 要说logo设计 xff0c pornhub的logo设计让人印象深刻 xff0c 黑底白字 xff0c 配上一小撮橙色 xff0c 给人极强的冲击力 这不 xff0c 有一个有意思的程序员弄了一个网站 xff0c 专门生成pornh
  • 大小端存储问题

    1 什么是数据的高低位 数据的高位在左 xff0c 低位在右 2 什么是内存的高低位 2 什么是大端存储 小端存储 简单记就是 xff1a 小端 xff1a 低低 xff08 数据低位在内存低位 xff09 大端 xff1a 高低 xff0
  • 【A星算法的优化方案】

    当地图很大的时候 xff0c 或者使用A星算法的寻路频率很高的时候 xff0c 普通的A星算法就会消耗大量的CPU性能急剧下降 xff0c 普通的A星性能还是不过关 接下来我们讲讲A星寻路在遇到性能瓶颈时的优化方案 一 长距离导航 当距离很
  • Java工具类:String与DateTime类型的相互转换

    1 String 转 DateTime 在转换之前需要引入 hutool 依赖 String datestr 61 34 2022 5 19 34 DateTime datetime 61 DateUtil parse datestr 2
  • Iterator迭代器的一般用法

    Iterator迭代器的一般用法 迭代器 xff08 Iterator xff09 迭代器是一种设计模式 xff0c 它是一个对象 xff0c 它可以遍历并选择序列中的对象 xff0c 而开发人员不需要了解该序列的底层结构 迭代器通常被称为
  • socket编程---fgets和fputs函数使用理解

    这一节是继续上一节socket05的讨论 xff0c 来探讨在使用socket进行通信中遇到的一些函数使用理解误区 1 fgets的使用注意点 在写socket通信 xff08 代码见上一篇中 xff0c 只是将sendbuf和recvbu