使用select 封装 socket api

来源:互联网 发布:猪八戒 知乎 编辑:程序博客网 时间:2024/06/02 22:52

common_socket.h

#ifndef __COMMSOCKET_H_#define __COMMSOCKET_H_#ifdef __cplusplusextern 'C'{#endiftypedef 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 = ETIMEDOUTif (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.cpp

#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); //返回已连接套接字elseret = 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_ */




0 0
原创粉丝点击