使用select 封装 socket api
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