天天看點

使用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.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); //傳回已連接配接套接字
	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_ */
           

繼續閱讀