首页 > 其他分享 >使用select 封装 socket api

使用select 封装 socket api

时间:2023-03-10 16:12:19浏览次数:47  
标签:return socket int ret api fd timeout time select

common_socket.h

#ifndef __COMMSOCKET_H_
#define __COMMSOCKET_H_
 
 
 
#ifdef __cplusplus
extern 'C'
{
#endif
 
        
typedef struct _socket_handle
{
    int socketfd;
    int conn_time;
    int send_time;
    int recv_time;
}Socket_handle;
 
 
 
#define Sck_ok 0
#define Sck_BaseError 3000
 
#define Sck_parmError         (Sck_BaseError+1)
#define Sck_TimeOutError     (Sck_BaseError+2)
#define Sck_MallocError     (Sck_BaseError+3)
 
 
int clt_socket_init(void **handle,int conn_time,int send_time,int recv_time);
 
int clt_socket_getconn(void *handle, char *ip, int port, int *connfd);
 
int clt_socket_send(void *handle,int connfd, unsigned char* buf,int lenght);
 
int clt_socket_recv(void *handle,int connfd, unsigned char* buf,int *lenght);
 
int clt_socket_close(void *handle);
 
#ifdef __cplusplus
}
#endif
 
 
#endif//__COMMSOCKET_H_

common_socket.c

int clt_socket_init(void **handle,int conn_time,int send_time,int recv_time)
{
    int ret;
    if(handle == NULL || conn_time < 0 || send_time < 0 || recv_time < 0)
    {
        ret = Sck_parmError;
        printf("parm error ret = %d,handle == NULL || conn_time < 0 || send_time < 0 || recv_time < 0\n",ret);
        return ret;
    }
    Socket_handle* tmp = (Socket_handle*) malloc(sizeof(handle));
    if(tmp == NULL)
    {
        ret = Sck_MallocError;
        printf("parm error malloc,ret = %d\n",ret);
        return ret;
    }
    
    tmp->conn_time = conn_time;
    tmp->send_time = send_time;
    tmp->recv_time = recv_time;
    
    *handle = tmp;
    
    return 0;
}
int clt_socket_getconn(void *handle, char *ip, int port, int *connfd)
{
    int ret = 0;
    if (handle == NULL || ip == NULL || connfd == NULL || port < 0 || port > 65537)
    {
        ret = Sck_parmError;
        printf("parm error ret = %d,handle == NULL || conn_time < 0 || send_time < 0 || recv_time < 0\n",ret);
        return ret;
    }
    Socket_handle *tmp = (Socket_handle*)handle;
    int socketfd;
    if ((socketfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    {
        ret = errno;
        printf("socket error,ret = %d\n",ret);
        return ret;
    }
    tmp->socketfd = socketfd;
 
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(port);
    servaddr.sin_addr.s_addr = inet_addr(ip);
    
    ret = connect_timeout(socketfd,&servaddr, tmp->conn_time);
    if (ret < 0)
    {
        if (ret==-1 && errno == ETIMEDOUT)
        {
            ret = Sck_TimeOutError;
            return ret;
        }
        else
        {
            printf("func connect_timeout() err:  %d\n", ret);
            return -1;
        }
    }
    
    *connfd = socketfd;
    
    return 0;
}

 

 

int clt_socket_send(void *handle,int connfd,unsigned char* buf,int datalen)
{
    int ret = 0;
    if (handle == NULL || connfd <= 0 || buf == NULL || datalen < 0)
    {
        ret = Sck_parmError;
        printf("parm error ret = %d,handle == NULL || connfd <= 0 || buf == NULL || datalen < 0\n",ret);
        return ret;
    }
    Socket_handle *tmp = (Socket_handle *)handle;
    
    ret = write_timeout(connfd,tmp->send_time);
    if(ret == 0)
    {
        int writed = 0;
        unsigned char *netdata = ( unsigned char *)malloc(datalen + 4);
        if ( netdata == NULL)
        {
            ret = Sck_MallocError;
            printf("func sckClient_send() mlloc Err:%d\n ", ret);
            return ret;
        }
        int netlen = htonl(datalen);
        memcpy(netdata,&netlen,4);
        memcpy(netdata+4,buf,datalen);
        
        writed = writen(connfd, netdata, datalen + 4);
        if (writed < (datalen + 4) )
        {
            if (netdata != NULL) 
            {
                free(netdata);
                netdata = NULL;
            }
            return writed;
        }
    }
    if(ret < 0)
    {
        //失败返回-1,超时返回-1并且errno = ETIMEDOUT
        if (ret == -1 && errno == ETIMEDOUT)
        {
            ret = Sck_TimeOutError;
            printf("func sckClient_send() mlloc Err:%d\n ", ret);
            return ret;
        }
        return ret;
    }
    
    return 0;
}

 

 

select

    用select封装超时(connect ,accept read,write)

sockutil.c

#include "sckutil.h"
 
/* read函数的调用方法
int ret;
ret = read_timeout(fd, 5);
if (ret == 0)
{
    read(fd, ...);
}
else if (ret == -1 && errno == ETIMEDOUT)
{
    timeout....
}
else
{
    ERR_EXIT("read_timeout");
}
*/
 
/**
 * read_timeout - 读超时检测函数,不含读操作
 * @fd: 文件描述符
 * @wait_seconds: 等待超时秒数,如果为0表示不检测超时
 * 成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
 */
int read_timeout(int fd, unsigned int wait_seconds)
{
    int ret = 0;
    if (wait_seconds > 0)
    {
        fd_set read_fdset;
        struct timeval timeout;
 
        FD_ZERO(&read_fdset);
        FD_SET(fd, &read_fdset);
 
        timeout.tv_sec = wait_seconds;
        timeout.tv_usec = 0;
        
        //select返回值三态
        //1 若timeout时间到(超时),没有检测到读事件 ret返回=0
        //2 若ret返回<0 &&  errno == EINTR 说明select的过程中被别的信号中断(可中断睡眠原理)
        //2-1 若返回-1,select出错
        //3 若ret返回值>0 表示有read事件发生,返回事件发生的个数
        
        do
        {
            ret = select(fd + 1, &read_fdset, NULL, NULL, &timeout);
        } while (ret < 0 && errno == EINTR);
 
        if (ret == 0)
        {
            ret = -1;
            errno = ETIMEDOUT;
        }
        else if (ret == 1)
            ret = 0;
    }
 
    return ret;
}
 
/**
 * write_timeout - 写超时检测函数,不含写操作
 * @fd: 文件描述符
 * @wait_seconds: 等待超时秒数,如果为0表示不检测超时
 * 成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
 */
int write_timeout(int fd, unsigned int wait_seconds)
{
    int ret = 0;
    if (wait_seconds > 0)
    {
        fd_set write_fdset;
        struct timeval timeout;
 
        FD_ZERO(&write_fdset);
        FD_SET(fd, &write_fdset);
 
        timeout.tv_sec = wait_seconds;
        timeout.tv_usec = 0;
        do
        {
            ret = select(fd + 1, NULL, &write_fdset, NULL, &timeout);
        } while (ret < 0 && errno == EINTR);
 
        if (ret == 0)
        {
            ret = -1;
            errno = ETIMEDOUT;
        }
        else if (ret == 1)
            ret = 0;
    }
 
    return ret;
}
 
/**
 * accept_timeout - 带超时的accept
 * @fd: 套接字
 * @addr: 输出参数,返回对方地址
 * @wait_seconds: 等待超时秒数,如果为0表示正常模式
 * 成功(未超时)返回已连接套接字,超时返回-1并且errno = ETIMEDOUT
 */
int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
    int ret;
    socklen_t addrlen = sizeof(struct sockaddr_in);
 
    if (wait_seconds > 0)
    {
        fd_set accept_fdset;
        struct timeval timeout;
        FD_ZERO(&accept_fdset);
        FD_SET(fd, &accept_fdset);
        timeout.tv_sec = wait_seconds;
        timeout.tv_usec = 0;
        do
        {
            ret = select(fd + 1, &accept_fdset, NULL, NULL, &timeout);
        } while (ret < 0 && errno == EINTR);
        if (ret == -1)
            return -1;
        else if (ret == 0)
        {
            errno = ETIMEDOUT;
            return -1;
        }
    }
 
    //一但检测出 有select事件发生,表示对等方完成了三次握手,客户端有新连接建立
    //此时再调用accept将不会堵塞
    if (addr != NULL)
        ret = accept(fd, (struct sockaddr*)addr, &addrlen); //返回已连接套接字
    else
        ret = accept(fd, NULL, NULL);
    if (ret == -1)
        ERR_EXIT("accept");
 
    return ret;
}
 
/**
 * activate_noblock - 设置I/O为非阻塞模式
 * @fd: 文件描符符
 */
void activate_nonblock(int fd)
{
    int ret;
    int flags = fcntl(fd, F_GETFL);
    if (flags == -1)
        ERR_EXIT("fcntl");
 
    flags |= O_NONBLOCK;
    ret = fcntl(fd, F_SETFL, flags);
    if (ret == -1)
        ERR_EXIT("fcntl");
}
 
/**
 * deactivate_nonblock - 设置I/O为阻塞模式 
 * @fd: 文件描符符
 */
void deactivate_nonblock(int fd)
{
    int ret;
    int flags = fcntl(fd, F_GETFL);
    if (flags == -1)
        ERR_EXIT("fcntl");
 
    flags &= ~O_NONBLOCK;
    ret = fcntl(fd, F_SETFL, flags);
    if (ret == -1)
        ERR_EXIT("fcntl");
}
 
 
/**
 * connect_timeout - connect
 * @fd: 套接字
 * @addr: 要连接的对方地址
 * @wait_seconds: 等待超时秒数,如果为0表示正常模式
 * 成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
 */
int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
    int ret;
    socklen_t addrlen = sizeof(struct sockaddr_in);
 
    if (wait_seconds > 0)
        activate_nonblock(fd);
 
    ret = connect(fd, (struct sockaddr*)addr, addrlen);
    if (ret < 0 && errno == EINPROGRESS)
    {
        //printf("11111111111111111111\n");
        fd_set connect_fdset;
        struct timeval timeout;
        FD_ZERO(&connect_fdset);
        FD_SET(fd, &connect_fdset);
        timeout.tv_sec = wait_seconds;
        timeout.tv_usec = 0;
        do
        {
            // 一但连接建立,则套接字就可写  所以connect_fdset放在了写集合中
            ret = select(fd + 1, NULL, &connect_fdset, NULL, &timeout);
        } while (ret < 0 && errno == EINTR);
        if (ret == 0)
        {
            ret = -1;
            errno = ETIMEDOUT;
        }
        else if (ret < 0)
            return -1;
        else if (ret == 1)
        {
            //printf("22222222222222222\n");
            /* ret返回为1(表示套接字可写),可能有两种情况,一种是连接建立成功,一种是套接字产生错误,*/
            /* 此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取。 */
            int err;
            socklen_t socklen = sizeof(err);
            int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &socklen);
            if (sockoptret == -1)
            {
                
                return -1;
            }
            if (err == 0)
            {
                //printf("3333333333333\n");
                ret = 0;
            }
            else
            {
                //printf("4444444444444444:%d\n", err);
                errno = err;
                ret = -1;
            }
        }
    }
    if (wait_seconds > 0)
    {
        deactivate_nonblock(fd);
    }
    return ret;
}
 
/**
 * readn - 读取固定字节数
 * @fd: 文件描述符
 * @buf: 接收缓冲区
 * @count: 要读取的字节数
 * 成功返回count,失败返回-1,读到EOF返回<count
 */
ssize_t readn(int fd, void *buf, size_t count)
{
    size_t nleft = count;
    ssize_t nread;
    char *bufp = (char*)buf;
 
    while (nleft > 0)
    {
        if ((nread = read(fd, bufp, nleft)) < 0)
        {
            if (errno == EINTR)
                continue;
            return -1;
        }
        else if (nread == 0)
            return count - nleft;
 
        bufp += nread;
        nleft -= nread;
    }
 
    return count;
}
 
/**
 * writen - 发送固定字节数
 * @fd: 文件描述符
 * @buf: 发送缓冲区
 * @count: 要读取的字节数
 * 成功返回count,失败返回-1
 */
ssize_t writen(int fd, const void *buf, size_t count)
{
    size_t nleft = count;
    ssize_t nwritten;
    char *bufp = (char*)buf;
 
    while (nleft > 0)
    {
        if ((nwritten = write(fd, bufp, nleft)) < 0)
        {
            if (errno == EINTR)
                continue;
            return -1;
        }
        else if (nwritten == 0)
            continue;
 
        bufp += nwritten;
        nleft -= nwritten;
    }
 
    return count;
}
 
/**
 * recv_peek - 仅仅查看套接字缓冲区数据,但不移除数据
 * @sockfd: 套接字
 * @buf: 接收缓冲区
 * @len: 长度
 * 成功返回>=0,失败返回-1
 */
ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
    while (1)
    {
        int ret = recv(sockfd, buf, len, MSG_PEEK);
        if (ret == -1 && errno == EINTR)
            continue;
        return ret;
    }
}
 
/**
 * readline - 按行读取数据
 * @sockfd: 套接字
 * @buf: 接收缓冲区
 * @maxline: 每行最大长度
 * 成功返回>=0,失败返回-1
 */
ssize_t readline(int sockfd, void *buf, size_t maxline)
{
    int ret;
    int nread;
    char *bufp = 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')
            {
                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;
}

 

 sockutil.h

#ifndef _SCK_UTIL_H_
#define _SCK_UTIL_H_
 
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
 
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
 
#define ERR_EXIT(m) \
  do \
  { \
    perror(m); \
    exit(EXIT_FAILURE); \
  } \
  while (0)
 
void activate_nonblock(int fd);//
 
void deactivate_nonblock(int fd);
 
int read_timeout(int fd, unsigned int wait_seconds);
int write_timeout(int fd, unsigned int wait_seconds);
int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds);
int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds);
 
 
ssize_t readn(int fd, void *buf, size_t count);
ssize_t writen(int fd, const void *buf, size_t count);
ssize_t recv_peek(int sockfd, void *buf, size_t len);
ssize_t readline(int sockfd, void *buf, size_t maxline);
 
#endif /* _SYS_UTIL_H_ */

 

 

 https://blog.csdn.net/u014338577/article/details/52904423

 

标签:return,socket,int,ret,api,fd,timeout,time,select
From: https://www.cnblogs.com/miwaiwai/p/17203728.html

相关文章

  • C# SelectMany的用法
    1、建立如下类:///<summary>///年级///</summary>publicclassGrade{///<summary>///年级名///</summary>......
  • ABP 动态 WebApi 隐藏接口的方法(一)
    在ABP实际开发过程中既有可能会遇到不希望将某些方法暴露,那么就需要想办法将接口隐藏起来。方法一:通过修改修饰符实现。例如将方法修改为private,这种方式比较常用。但这......
  • yapi - 接口管理平台搭建
    YApi是比较好用的接口管理平台,通过docker-compose搭建记录。准备工作CentOSLinuxrelease7.9.2009(Core)Docker20.10.8搭建1.创建容器网络$dockernetwor......
  • 如何在 Net6.0 中对 WebAPI 进行 JWT 认证和授权
    一、简介我们做微服务开发,或者说做分布式开发,有一项技术我们是避不开的,那就是WebAPI,在Net6.0中,有两类WebAPI,一类是极简WebAPI,它砍掉了很多冗余的东西,更纯粹的......
  • VUE-select-option 获取 label value 值
    1.绑定的value值中多赋值一个参数:value="{value:item.value,label:item.label}"2.添加@change事件进行获取值<template><el-selectv-model="value"placehol......
  • 如何在 Net6.0 中对 WebAPI 进行 JWT 认证和授权
    一、简介我们做微服务开发,或者说做分布式开发,有一项技术我们是避不开的,那就是WebAPI,在Net6.0中,有两类WebAPI,一类是极简WebAPI,它砍掉了很多冗余的东西,更纯粹的是......
  • K8S 性能优化 - K8S APIServer 调优
    前言K8S性能优化系列文章,本文为第二篇:KubernetesAPIServer性能优化参数最佳实践。系列文章:《K8S性能优化-OSsysctl调优》参数一览kube-apiserver推荐优化......
  • ORM API
    ORMAPI模块模块字段被定义为模块本身的属性,默认情况下,该字段的标签名(即用户可见的名称)是该字段名称的大写版本,但是可以用string参数覆盖。field2=fields.Integer(st......
  • API发布与部署-- .NET WebAPI 篇
    API发布-- .NET  WebAPI 篇1:准备工作:设置数据库链接字符串,打开一个正常的项目:首先注意所有的appsettings.json文件,包括生产环境配置子文件,注意里面的链......
  • unityAPI01
    1.创建一个新的空游戏对象,将代码挂载到它身上2.创建脚本3.将脚本挂载到物体身上打印函数voidStart(){print("我们正式进入API的学习");Debu......