LINUX C中的输入和输出使用方法!(结合在socket中的应用)

1 gets与fgets每行一次的IO

函数原型

char *gets(char *buf)
char *fgets(char *buf,int n,FILE *fp)

gets标准输入读,fgets从指定流中读。

gets是一个不推荐使用的函数,理由是可能造成缓冲区的溢出。

fgets从指定流中一直读到换行符为止(包括换行符),读入的数据放入buf中。缓冲区的数据以NULL字符结尾!也就是说会多写一个字符(NULL)。如果待读的数据比n要大,则只读取n-1个字节的数据,最后以NULL结尾,下次调用会继续读该行。

说明,因为读到的数据以NULL结尾,那么就可以使用strlen(buf)获得所读取的数据的大小。strlen接受一个char*类型的指针,计算出从这个地址开始直到NULL(即\0)的长度,不包括NULL。

2 puts与fputs

函数原型

int puts(const char *str)
int fputs(const chat *str,FILE *fp)

fputs将一个以NULL(即\0)字符终止的字符串写入指定的流,尾端的NULL不输出。
puts将一个以NULL结尾的字符串写到标准输出,终止符不写出。但是,与fputs不同的是,随后puts又将一个换行符写到标准输出。
puts()和 printf的用法一样,puts()函数的作用与语句“printf("%s\n",s);的作用相同。注意:puts在输出字 符串后会自动输出一个回车符。

**所以尽量配对的使用fgets和fputs!!!**尽量不用gets和puts

虽然这是每行IO,但是他的输出和行却一点关系没有,puts和fputs可以看作仅仅是一个字符串输出工具。

3 snprintf

int snprintf(char *str, size_t size, const char *format, ...);

将可变个参数(…)按照format格式化成字符串,然后将其复制到str中
(1) 如果格式化后的字符串长度 < size,则将此字符串全部复制到str中,并给其后添加一个字符串结束符(’\0’);
(2) 如果格式化后的字符串长度 => size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符(’\0’)

函数返回值:若成功则返回欲写入的字符串长度,若出错则返回负值。

4 read读文件,结束符是什么,当read遇到‘\0’会怎么样 ?

注意:read和write是不区分’\0’的!他们读写只会按照给定的字节读写,没有结束标志符。停止读写的原因只能是缓冲区超出,或者缓冲区为零!总之不受’\0’的影响。

其实,可以把文件的内容看做是字符串!读写文件就是读写里面的字符(一个字节大小).

这里结合socket,给出一个实例,验证linux中输入输出函数的特性:
这个例子是回射程序,客户端在终端上中输出一行数据,然后服务器接受这个数据,再将其返回,客户端收到后再显示在终端上。

//echo_client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>

#define BUFSIZE 512

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

    if (argc!=2)
    {
        printf("argc is not 2\n");
        exit(1);
    }

    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(2222);
    if(inet_pton(AF_INET,argv[1],&servaddr.sin_addr)<0)
    {
        perror("inet_pton error\n");
        exit(1);
    }
    
    sd=socket(AF_INET,SOCK_STREAM,0);

    if(connect(sd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
    {
        perror("connect is error\n");
        exit(1);
    }

    str_cli(stdin,sd);
    exit(0);
}

void str_cli(FILE * fp,int sockfd)
{
    char sendline[BUFSIZE];
    char receline[BUFSIZE];
    
    char a;
    int n,n2;
    sendline[8]='8';
    sendline[9]='9';
    while(1)
    {
        fgets(sendline,BUFSIZE,fp);//输入123后回车,实际写入5个字节:"123\n\0",因为fgets不会丢弃换行符!
        printf("strlen of senline is %ld",strlen(sendline));//结果是4,strlen遇到NUll就结束
        printf("the neirong of sendline is %s",sendline);//此时可知sendline中的内容是:“123\n\0xxxxxxxx...” x指的是系统的随机数。,但这里输出是123\n,因为%s遇到NULL就结束
        n=write(sockfd,sendline,strlen(sendline)+1);//写进去5个字节,也就是“123\n\0”,这里不能写strlen(senline),因为没有将结束符写进去,这样会导致后面服务器发来的消息也没有结束符,所以后面的fputs就会找不到在哪结束!!!
        printf("the n write is %d",n);//n=5
        n2=read(sockfd,receline,BUFSIZE);
        
        printf("the num read n2 is %d",n2);
        
        fputs(receline1,stdout);

    }
    return ;
}
//echo_server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include <time.h>
#define BUFSIZE 1024
void str_echo(int confd);
int main()
{
    int sd;
    pid_t pid;
    int confd;
    socklen_t len;
    time_t tick;
    char buf[BUFSIZE];
    char buf_addr[BUFSIZE];
    struct sockaddr_in cliaddr,servaddr;

    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(2222);
    //servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
    inet_pton(AF_INET,"0.0.0.0",&servaddr.sin_addr.s_addr);//带或不带s_addr都可以

    sd=socket(AF_INET,SOCK_STREAM,0);
    if(sd<0)
    {
        perror("socket is error\n");
        exit(1);
    }
    if(bind(sd,(struct sockaddr *)&servaddr,sizeof(servaddr))<0)
    {
        perror("bind error\n");
        close(sd);//实际上exit会自动关闭所有的文件描述符,所以不写这一步也可以
        exit(1);
    }
    if(listen(sd,5)<0)
    {
        perror("listen is error\n");
        close(sd);
        exit(1);
    }

    while(1)
    {
        len=sizeof(cliaddr);
        confd=accept(sd,(struct sockaddr *)&cliaddr,&len);
        
        //打印对端客户端信息,IP地址和端口
        printf("connection from :%s,port is %d\n", inet_ntop(AF_INET,&cliaddr.sin_addr.s_addr,buf_addr,sizeof(buf_addr)),ntohs(cliaddr.sin_port));

        pid=fork();
        if(pid==0)
        {
            close(sd);
            str_echo(confd);
            exit(0);
        }
        
        close(confd);
    }
}

void str_echo(int confd)
{
    
    int n;
    char buf[BUFSIZE];
    while(1)
    {
        n=read(confd,buf,BUFSIZE);//会读到5个字节,因为客户端只写入了5个字节,read读完缓冲区就返回。
        printf("n is %d\n",n);//n=5
        print("read byte is %s",buf);//此时buf中的内容会是“123\n\0xxxxxxxx...”,但是这一行的输出会是123\n,因为%s遇到NULL就停下
        if(n<0 && errno==EINTR)
        {
            continue;
        }
        else if(n>0)
        {
            //write(confd,buf,BUFSIZE);//这个地方一定不能写入BUFSIZE个数据!!!,否则客户端的read会把这1024(BUFSIZE的大小)全部读出!!!
            write(confd,buf,n);//这才是正确的,读到几个字节,返回几个字节
        }
        else
        {
            perror("read error:\n");
            exit(1);
        }
    
    }
    return ;
}

注意:

  • 这里牵涉待的一个点就是,读的时候可以读BUFSIZE个字节的数据,但是写的时候最好加上结束符,这样的话,就是写入固定大小的数据,read不用读完BUFSIZE个字节就能读完缓冲区返回!

  • 所以,以后规定,读可以读BUFSZIE,但是写的内容要严格规范,不能简单的写进BUFSZIE个字节,否则,read会没完没了的读!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值