TCP之函数封装

本文所有函数皆是为实现 TCP之简单回传(二)   系列所封装的函数;

所有函数皆用C语言实现。函数以及注释如下:

头文件:

//.h
#ifndef SYSUTIL_H
#define SYSUTIL_H

#include <stdint.h>
#include <sys/types.h>
void nano_sleep(double val); //实现定时作用
ssize_t readn(int fd, void *buf, size_t count);//读取真实数据
ssize_t writen(int fd, const void *buf, size_t count);//写所读来的数据
ssize_t readline(int fd, void *usrbuf, size_t maxlen);//读数据(解决粘包问题)
ssize_t readline_slow(int fd, void *usrbuf, size_t maxlen);//读数据--->效率低下
void send_int32(int sockfd, int32_t val);//发送一个int
int32_t recv_int32(int sockfd); //接收一个int 为后来的readn读入精准的数据做准备

#endif

具体实现:

/.c
#include "sysutil.h"
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

//睡眠时间
void nano_sleep(double val)
{
    struct timespec tv;
    memset(&tv, 0, sizeof(tv));
    tv.tv_sec = val;//取整
    tv.tv_nsec = (val- tv.tv_sec)*1000*1000*1000;

    int ret;
    do
    {
        ret = nanosleep(&tv, &tv);
    }while(ret == -1 && errno ==EINTR);//若是被中断信号打断,则继续执行sleep
}
//告诉server,本次发送数据的真实长度val
void send_int32(int sockfd, int32_t val)
{
    int32_t tmp = htonl(val);//转化为网络字节序
    if(writen(sockfd, &tmp, sizeof(int32_t)) != sizeof(int32_t))//发送给server
        ERR_EXIT("write");
}
//接收一个int型数据,以确保精确接收
int32_t recv_int32(int sockfd)
{
    int32_t tmp;
    if(readn(sockfd, &tmp, sizeof(int32_t))!= sizeof(int32_t))
        ERR_EXIT("read");
    return ntohl(tmp);//网络序转化为主机序。
}

//server读取数据
ssize_t readn(int fd, void *buf, size_t count)
{
    size_t nleft = count;  //剩余字符数
    ssize_t nread;//用于返回值
    char *pbuf = (char*)buf;

    while(nleft > 0)
    {
        nread = read(fd, pbuf, nleft);//发送数据
        if( nread == -1)
        {
            if(errno == EINTR)//被中断信号打断
                continue;
            return -1 ; //err
        }else if( nread == 0)
        {
            break; //读完
        }
        nleft = nleft - nread;//剩余字符数
        pbuf = pbuf + nread;//下次的偏移位置
    }
    return (count-nleft) ;//attentin 两个条件退出循环
}

//client向server写数据
ssize_t writen(int fd, const void* buf, size_t count)
{
    size_t nleft = count ;//剩余字节流
    ssize_t nwrite;//return
    const char *pbuf =(const char*)buf;

    while(nleft > 0)
    {
        nwrite = write( fd, pbuf, nleft);//写数据
        if(nwrite <= 0)//err
        {
            if(nwrite == -1 && errno == EINTR)
                continue;
            return -1;
        }

        nleft = nleft - nwrite;//剩余字节流
        pbuf = pbuf + nwrite;//偏移位置
    }

    return count;
}

//预览内核缓冲区数据
ssize_t recv_peek(int fd, void *usrbuf, size_t maxlen)
{
    ssize_t nread;
    do
    {
        nread = recv(fd, usrbuf, maxlen, MSG_PEEK);
    }
    while(nread == -1 && errno == EINTR);
    return nread;
}

ssize_t readline(int fd, void *usrbuf, size_t maxlen)
{
    char *bufp = (char *)usrbuf;
    size_t nleft = maxlen - 1;
    ssize_t count = 0;

    ssize_t  nread;
    while(nleft > 0)
    {
        nread = recv_peek(fd, bufp, nleft);//预览内核缓冲区数据
        if( nread <= 0)  //由客户端处理
            return nread;
        //遍历bufp,以确定是否存在\n
        int i;
        for ( i = 0; i < nread; i++)
        {
        //存在‘\n‘
            if(bufp[i] == ‘\n‘)
            {
                size_t nsize = i +1;
                if( readn(fd, bufp, nsize) != nsize)//说明\n前有i个字符
                    ERR_EXIT("readn");
                bufp +=nsize; //重置偏移量
                count +=nsize;//统计读取个数
                *bufp = 0;
                return count;
            }
        }
        //不存在‘\n‘
        if( readn(fd, bufp, nread) != nread)
            ERR_EXIT("readn");
        bufp += nread;
        count += nread;
        nleft -=nread;
    }
    *bufp = 0;
    return count;
}

//按字符读取--->由于每读取一个字符就产生一次系统调用,故效率较低
ssize_t readline_slow(int fd, void *usrbuf, size_t maxlen)
{
    char *bufp = (char*)usrbuf;//偏移位置
    ssize_t nread;
    size_t nleft = maxlen -1 ;//剩余字节数
    char ch;//保存每次读取的字符
    while( nleft > 0)//只要还有没读的,就一直循环
    {
        if(-1 == (nread=read(fd, &ch, 1)))//把从fd中读取的字符存进ch中
        {
            if(errno == EINTR)//被中断信号打断
                continue;
            return -1;//err
        }else if(0 == nread )
            break; //EOF

        *bufp = ch;//将读取的字符存进buf
        bufp++;//向前移动

        nleft --;//剩余字节数--
        if(ch == ‘\n‘)//如果该字符为\n。本次读取完成
            break;
    }
    *bufp =‘\0‘;//
    return (maxlen- nleft -1);//最大长度 -剩余的字符 - ‘\0‘
}
时间: 2024-08-25 00:37:20

TCP之函数封装的相关文章

div盒子的缓动函数封装

1.2.  缓动动画 1.2.1.       原理公式 动画公式 leader = leader + step 匀速动画公式 step = 定值 leader = leader + step 缓动动画公式 step = ( target - leader ) / 10 leader = leader + step 缓动动画的好处 他的移动是有尽头的.不像基础匀速运动那样无限移动. 有非常逼真的缓动效果,实现的动画效果更细腻. 如果不清除定时器,物体永远跟着目标leader在移动. @体验缓动动

用工厂函数封装对象

工厂函数是在方法中建立一个Object空对象,然后再对此对象添加属性和方法,然后把Object对象返回 <html> <head> <title>工厂函数封装对象</title> <script type="text/javascript"> // 工厂函数模式封装对象 //特点:返回结果为对象的函数,在用工厂函数创建对象的时候,一定要new function fn(nianling){ var obj=new object(

第5章 用函数封装程序功能

第5章 用函数封装程序功能 5.1 函数就是一个大"箱子" 5.1.1 函数的声明和定义 5.1.2 函数调用机制 5.1.3 函数的声明与函数调用 5.1.4 函数参数的传递 5.1.5 函数的返回值 5.2 内联函数 5.2.1 用体积换速度的内联函数 5.2.2 内联函数的使用规则 5.3 重载函数 5.3.1 重载函数的声明 5.3.2 重载函数的解析 5.4 函数设计的基本规则 5.4.1 函数声明的设计规则 5.4.2 函数体的设计规则 TOP

dataSet==&gt;Ilist&lt;&gt;的函数封装

一般我们用使用ADO.NET查询数据库返回泛型集合?使用SqlDataReader逐行读取数据存入对象 代码 /// <summary> /// 获取UserInfo泛型集合 /// </summary> /// <param name="connStr">数据库连接字符串</param> /// <param name="sqlStr">要查询的T-SQL</param> /// <re

使用函数封装代码

函数是一个命名的独立代码段,它执行特定的任务,并可能给调用它的程序返回一个值. 函数原型--程序后面将出现的函数的模型.函数原型包括函数名称.传递给函数的变量列表 (实 参).函数返回变量的类型.函数原型以分号结尾: 如  long cube (long x); 函数定义的第一行为函数头,它与函数原型相同,只是没有分号.另外虽然在函数原型中,参数变量名是可选的,但在函数头中必须包含.函数头的后面是函数体,其中包含函数将要执行的语句.函数体以做花括号开始以右花括号结束.如果函数返回的类型不是voi

常见的公共函数封装方法(密码强度、手机号验证、邮箱验证、输入金额验证)

//密码复杂度公共函数封装(邮箱,手机号) this.PasswordStrength = function(password) { var rule = Auto517.config.passwordRule.rule; var min = Auto517.config.passwordRule.min; var max = Auto517.config.passwordRule.max; if(rule == 0 && eval('/^[0-9]{' + min + ',' + max

c++ 回调函数封装

std::function<void(int a,int b)> ha; //函数封装  当成参数用callback  std::bind(&fun1,this,std::placeholders::_1,std::placeholders::_2)  //函数回调时使用 方便之处:类型安全.

原生JavaScript实现的图片轮播左右滑动效果函数封装

原生js实现的图片轮播左右滑动效果函数封装 方便以后需要的时候拿出来复用. 适合新手学习使用. 使用方法轮播图的html结构就不多说了.不过有一点:为了实现无缝无限轮播,需要在三张图片的最前面和最后面再额外添加两张图片(见下),原理简单说就是当图片滑动到最后一张时立马跳到第二张,眼睛看上去就像前后无缝一样. 把img_slider.js引入html,然后编辑window.onload = (function () { ··· });中的内容. (获取图片床,按钮,标识等元素.然后调用slideI

XMLHttpRequest函数封装

XMLHttpRequest函数封装: function ajax(Url,sccuessFn,failureFn) { //1.创建XMLHttpRequest对象 var xhr = null; if(window.XMLHttpRequest) { xhr = new XMLHttpRequest(); }else { xhr = new ActiveXObject('Microsoft.XMLHTTP'); } //2.打开与服务器的链接 xhr = open('get',Url,tru