UNIX环境高级编程学习之第十六章网络IPC:套接字 – 非阻塞的Socket通信Poll模型(多路复用), 实用Socket通信模板

Linux/Unix C/C++ xiujie 300℃ 0评论 已收录

UNIX环境高级编程学习之第十六章网络IPC:套接字 – 非阻塞的Socket通信Poll模型(多路复用), 实用Socket通信模板

/* User:Lixiujie         
 * Date:20101206
 * Desc:Unix(Linux)非阻塞的Socket通信Poll模型,多路复用,TCP服务器端, 向客户端发送响应信息。
 * File:tcp_server_poll.c  
 * System:Ubuntu 64bit  
 * gcc tcp_server_poll.c -o  tcp_server_poll
 * tcp_server_poll 7878
 *
 * 
poll函数可用的测试值
POLLIN 普通或优先级带数据可读 
POLLRDNORM 普通数据可读 
POLLRDBAND 优先级带数据可读 
POLLPRI 高优先级数据可读 
POLLOUT 普通数据可写 
POLLWRNORM 普通数据可写 
POLLWRBAND 优先级带数据可写 
POLLERR 发生错误 
POLLHUP 发生挂起 
POLLNVAL 描述字不是一个打开的文件 

timeout值 说明 
-1 永远等待 
0 立即返回,不阻塞进程 
>0 等待指定数目的毫秒数 

 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#include <sys/types.h> 
#include <sys/socket.h> /* socket bind listen connect accept send recv */
#include <arpa/inet.h>  /* htons ntohs htonl ntohl inet_addr inet_ntoa */
#include <netinet/in.h> /* sockaddr_in */

#include <poll.h>

#define BUFLEN 1024
#define QLEN 20

/* 传送信息结构体 */
typedef struct _MyMsg{
	char szCmd[16];/* message command 
					* RE_LINK:test link request
					* RESP_LINK:test link response
					* RE_EXIT: exit request
					* RESP_TEXT: exit response
					* RE_DATA: data request
					* RESP_DATA: data response
					*/
	int  iLen;     /* message data length*/
	char szData[0];/* message data */
}MyMsg;

/* 信息处理 */
MyMsg * MsgProcess(MyMsg *pMsgIn){
	char szBuf[BUFLEN] = { 0x00 };
	char szTmp[BUFLEN] = { 0x00 };
	MyMsg *pMsg = NULL;
	FILE *fp;
	if (strcmp(pMsgIn->szCmd, "RE_LINK") == 0){
		pMsg = (MyMsg *)malloc(sizeof(MyMsg));
		memset(pMsg, 0, sizeof(MyMsg));
		strcpy(pMsg->szCmd, "RESP_LINK");
	}else if (strcmp(pMsgIn->szCmd, "RESP_LINK") == 0){
	}else if (strcmp(pMsgIn->szCmd, "RE_EXIT") == 0){
		pMsg = (MyMsg *)malloc(sizeof(MyMsg));
		memset(pMsg, 0, sizeof(MyMsg));
		strcpy(pMsg->szCmd, "RESP_TEXT");
	}else if (strcmp(pMsgIn->szCmd, "RESP_TEXT") == 0){
	}else if (strcmp(pMsgIn->szCmd, "RE_DATA") == 0){
		memset(szBuf, 0, BUFLEN);
		strncpy(szBuf, pMsgIn->szData, pMsgIn->iLen);
		if ((fp = popen(szBuf, "r")) == NULL){
			memset(szBuf, 0, BUFLEN);
			sprintf(szBuf, "error: %s/n", strerror(errno));
		}else{
			memset(szTmp, 0, BUFLEN);
			while (fgets(szTmp, BUFLEN, fp) != NULL){
				strcat(szBuf, szTmp);
				memset(szTmp, 0, BUFLEN);
			}
			pclose(fp);
		}
		pMsg = (MyMsg *)malloc(sizeof(MyMsg)+ strlen(szBuf)+1);
		memset(pMsg, 0, sizeof(MyMsg));
		strcpy(pMsg->szCmd, "RESP_DATA");
		pMsg->iLen = strlen(szBuf)+1;
		strcpy(pMsg->szData, szBuf);
	}else if (strcmp(pMsgIn->szCmd, "RESP_DATA") == 0){
	}
	return pMsg;
}

/* Socket结构体 */
typedef struct _SockNode{
	int sock;
	struct sockaddr_in addr;
	struct _SockNode *pNext;
}SockNode;

/* create SockNode */
SockNode* createSockNode(int sock, struct sockaddr_in *pAddr){
	SockNode *p = NULL;
	if ((p = (SockNode *)malloc(sizeof(SockNode))) != NULL){
		p->sock = sock;
		memcpy(&(p->addr), pAddr, sizeof(struct sockaddr_in));
		p->pNext = NULL;
	}
	return p;
}

/* add SockNode from list */
void addSockNodeList(SockNode *head, SockNode *node){
	SockNode *p = head;
	while (p->pNext != NULL){
		p = p->pNext;
	}
	p->pNext = node;
}

/* delete SockNode from list 
 * return head
 */
SockNode* deleteSockNodeList(SockNode *head, int sock){
	SockNode *p = head, *pPrevious = p;
	while (p != NULL){
		if (p->sock == sock){
			if (p != pPrevious){
				pPrevious->pNext = p->pNext;
			}else{
				head = p->pNext;
			}
			free(p);
			break;
		}
		pPrevious = p;
		p = p->pNext;
	}
	return head;
}
/* select SockNode from list 
 * return head
 */
SockNode* selectSockNodeList(SockNode *head, int sock){
	SockNode *p = head, *pPrevious = p;
	while (p != NULL){
		if (p->sock == sock){
			return p;
		}
		p = p->pNext;
	}
	return NULL;
}

/* maximumly sock from list */
int maxSockNodeList(SockNode *head){
	SockNode *p = head;
	int maxsock = -1;
	while (p != NULL){
		maxsock = maxsock > p->sock ? maxsock : p->sock;
		p = p->pNext;
	}
	return maxsock;
}

/* create tcp server */
int initserver(int type, const struct sockaddr *addr, socklen_t alen, int qlen){
	int fd;
	int err = 0, iSockAttrOn = 1;
	
	/* 创建 */
	if ((fd = socket(addr->sa_family, type, 0)) < 0){
		return -1;
	}
	/* 端口复用 */
	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &iSockAttrOn, sizeof(iSockAttrOn) ) < 0){
		err = errno;
		goto errout;
	}
	/* 绑定 */
	if (bind(fd, addr, alen) < 0){
		err = errno;
		goto errout;
	}
	/* 监听数 */
	if (SOCK_STREAM == type || SOCK_SEQPACKET == type){
		if (listen(fd, qlen) < 0) {
			err = errno;
			goto errout;
		}
	}
	return fd;
errout:
	close(fd);
	errno = err;
	return -1;
}

int main(int argc, char *argv[]){
	if (argc != 2){
		printf("arg err!/n");
		return -1;
	}
	int sefd, clfd, ret, len;
	char szBuf[BUFLEN];
	SockNode *head = NULL,*node = NULL; /* socket 监听链表 */

	struct sockaddr_in se_addr,cl_addr;
	socklen_t alen = sizeof(struct sockaddr);
	/* 设置服务IP和端口 */
	memset((void *)&se_addr, 0, alen);
	se_addr.sin_family = AF_INET;
	se_addr.sin_addr.s_addr = INADDR_ANY;// inet_addr("0.0.0.0");
	se_addr.sin_port = htons(atoi(argv[1]));
	if ((sefd = initserver(SOCK_STREAM, (struct sockaddr *)&se_addr, alen, QLEN)) < 0){
		printf("initserver err=%s!/n", strerror(errno));
		return -1;
	}
	printf("initserver OK !/n");
	head = createSockNode(sefd, &se_addr);
	struct pollfd fds[QLEN];/* 用于poll函数第一个参数的数组 */
	int nfds = 0;
	int i;
	while (1){
		node = head;
		nfds = 0;
		while(node != NULL){
			fds[nfds].fd = node->sock;
			fds[nfds].events = POLLIN | POLLPRI; /* 普通或优先级或高优先级带数据可读 */
			++nfds;
			node = node->pNext;
		}
		for (i = nfds;i < QLEN; i++){
			fds[i].fd = -1; /* 数组中的其它元素将暂时设置成不可用 */
		}
		printf("poll before OK !nfds=%d/n", nfds);
		ret = poll(fds, nfds, -1);
		printf("poll after OK !ret = %d/n", ret);
		if (ret < 0){
			printf("poll err=%s!/n", strerror(errno));
			while (head != NULL){
				node = head;
				head = head->pNext;
				close(node->sock);
				free(node);
			}
			return -1;
		}else if (0 == ret) { /* 不可能出现  */
			printf("poll timeout!/n");
			sleep(1);
			continue;
		}
		for (i = 0; i < nfds; i++){
			if (0 == i && ((fds[i].revents & POLLIN) || (fds[i].revents & POLLPRI))){
				alen = sizeof(struct sockaddr);
				memset((void *)&cl_addr, 0 , alen);
				clfd = accept(fds[i].fd, (struct sockaddr*)&cl_addr, &alen);
				if (clfd < 0){
					printf("accept err=%s!/n", strerror(errno));
					while (head != NULL){
						node = head;
						head = head->pNext;
						close(node->sock);
						free(node);
					}
					return -1;
				}
				printf("Client connect:ip=%s, port=%d /n", inet_ntoa(cl_addr.sin_addr), 
					ntohs(cl_addr.sin_port));
				addSockNodeList(head, createSockNode(clfd, &cl_addr));
			}else if ((fds[i].revents & POLLIN) || (fds[i].revents & POLLPRI)){
				node = selectSockNodeList(head, fds[i].fd);
				if (NULL == node){
					continue;
				}
				memset(szBuf, 0, BUFLEN);
				ret = recv(node->sock, szBuf, BUFLEN, 0);
				if (ret < 0){
					printf("recv Client err=%s, ip=%s, port=%d!/n", strerror(errno), 
						inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));
					close(node->sock);
					head =  deleteSockNodeList(head, node->sock);
				} else if (0 == ret){
					printf("recv Client exit, ip=%s, port=%d!/n", 
						inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));
					close(node->sock);
					head = deleteSockNodeList(head, node->sock);

				} else {
					MyMsg *msgRecv = (MyMsg *)szBuf;
					msgRecv->iLen = ntohl(msgRecv->iLen);/* 转换成本机字节序 */
					MyMsg *msgSend = NULL;
					/* 预处理 */
					if (strcmp(msgRecv->szCmd, "RE_LINK") == 0){
						printf("recv Client RE_LINK, ip=%s, port=%d!/n", 
							inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));
						msgSend = MsgProcess(msgRecv); /* 实际处理 */
						if (msgSend != NULL){
							len = msgSend->iLen;
							msgSend->iLen = htonl(msgSend->iLen); /* 转换成网络字节序 */
							memset(szBuf, 0, BUFLEN);
							memcpy(szBuf, msgSend, sizeof(MyMsg) + len);
							send(node->sock, szBuf, sizeof(MyMsg) + len, 0);
							printf("send Client %s, ip=%s, port=%d!/n", msgSend->szCmd, 
								inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));
							free(msgSend);
							msgSend = NULL;
						}
					}else if (strcmp(msgRecv->szCmd, "RESP_LINK") == 0){
						printf("recv Client RESP_LINK, ip=%s, port=%d!/n", 
							inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));
					}else if (strcmp(msgRecv->szCmd, "RE_EXIT") == 0){
						printf("recv Client RE_EXIT, ip=%s, port=%d!/n", 
							inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));
						msgSend = MsgProcess(msgRecv); /* 实际处理 */
						if (msgSend != NULL){
							len = msgSend->iLen;
							msgSend->iLen = htonl(msgSend->iLen); /* 转换成网络字节序 */
							memset(szBuf, 0, BUFLEN);
							memcpy(szBuf, msgSend, sizeof(MyMsg) + len);
							send(node->sock, szBuf, sizeof(MyMsg) + len, 0);
							printf("send Client %s, ip=%s, port=%d!/n", msgSend->szCmd, 
								inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));
							free(msgSend);
							msgSend = NULL;
							
						}
						close(node->sock);
						head = deleteSockNodeList(head, node->sock);
					}else if (strcmp(msgRecv->szCmd, "RESP_TEXT") == 0){
						printf("recv Client RESP_TEXT, ip=%s, port=%d!/n", 
							inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port));
						close(node->sock);
						head = deleteSockNodeList(head, node->sock);
					}else if (strcmp(msgRecv->szCmd, "RE_DATA") == 0){
						printf("recv Client RE_DATA, ip=%s, port=%d, data:%s!/n", 
							inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port), msgRecv->szData);
						msgSend = MsgProcess(msgRecv); /* 实际处理 */
						if (msgSend != NULL){
							len = msgSend->iLen;
							msgSend->iLen = htonl(msgSend->iLen); /* 转换成网络字节序 */
							memset(szBuf, 0, BUFLEN);
							memcpy(szBuf, msgSend, sizeof(MyMsg) + len);
							send(node->sock, szBuf, sizeof(MyMsg) + len, 0);
							printf("send Client %s, ip=%s, port=%d, data:%s!/n", msgSend->szCmd, 
								inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port), 
								len > 0 ? msgSend->szData : "");
							free(msgSend);
							msgSend = NULL;
						}
					}else if (strcmp(msgRecv->szCmd, "RESP_DATA") == 0){
						printf("recv Client RESP_DATA, ip=%s, port=%d, data:%s!/n", 
							inet_ntoa(node->addr.sin_addr), ntohs(node->addr.sin_port), msgRecv->szData);
					}
				}/* recv */
			}/* if i = 0 */
		}/*for */
	}/* while 1 */

	while (head != NULL){
		node = head;
		head = head->pNext;
		close(node->sock);
		free(node);
	}
	return 0;
}



喜欢 (0)
发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址