select练习小结

原创 2015年07月10日 21:38:17
<pre name="code" class="cpp">//readwrite.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#define MAXLEN 1024

struct packet
{
	int len;
	char content[MAXLEN];
};

size_t readn(int fd, void *buf, size_t len)
{
    size_t left = len;
    int ret;
    char *pbuf = (char *)buf;

    while(left > 0)
    {
    	ret = read(fd, pbuf, left);
    	if(ret < 0)
    	{
    		if(errno == EINTR)        //遇到中断则继续
    			continue;
    		return -1;
    	}
	    else if(ret == 0)
	    {
	    	return len - left;//对等方结束发送
	    }
	    	left -= ret;
	    	pbuf += ret;
	}
	return len;
}

size_t writen(int fd, void *buf, size_t len)
{
    size_t left = len;
    int ret;
    char *pbuf = (char *)buf;

    while(left > 0)
    {
	    ret = write(fd, pbuf, left);
	    if(ret < 0)
	    {
	    	if (errno == EINTR)
	    	continue;
	    	return 1;
	    }
	    else if(ret == 0)
	    {
	    	continue; // 没有写入,则继续
	    }
	    left -= ret;
	    pbuf += ret;
    }
    return len;
}
// client.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "readwrite.h"

size_t doit(int sock)
{
    fd_set rset;
    FD_ZERO(&rset);

    int fdmax;
    int nready;
    int fd_stdin = fileno(stdin);
    fdmax = sock > fd_stdin ? sock : fd_stdin;
    int n;
    struct packet recvBuf;
    struct packet sendBuf;

    while(1)
    {
	    FD_SET(sock, &rset);
	    FD_SET(fd_stdin, &rset);
	    nready = select(fdmax + 1, &rset, NULL, NULL, NULL);

	    if(nready == -1)
	    {
	    	perror("select return -1.");
	    	exit(1);
	    }
	    else if(nready == 0)
	    	continue;// actually, nready == 0 would NEVER happen, because timeval is NULL
	    else
	    {
	    	if(FD_ISSET(sock, &rset))
	    	{
	    		memset(&recvBuf, 0, sizeof(recvBuf));
	    		int ret = readn(sock, &recvBuf.len, 4);
	    		if(ret == -1)
	    		{
	    			perror("sock ret -1");
	    			exit(-1);
	    		}
	    		else if(ret < 4)
	    		{
	    			perror("client close.");
	    			break;
	   			}

	    		n = ntohl(recvBuf.len);
	    		ret = readn(sock, recvBuf.content, n);

			    if(ret == -1)
			    {
			    	perror("sock ret -1");
			    	exit(-1);
			    }
			    else if(ret < n)
			    {
			    	perror("client close.");
			    	break;
			    }
	    		fputs(recvBuf.content, stdout);
	    	}
		    if(FD_ISSET(fd_stdin, &rset))
		    {
		    	if(fgets(sendBuf.content, sizeof(sendBuf.content), stdin) == NULL)
		    		break;
		    	n = strlen(sendBuf.content);
		   	 	sendBuf.len = htonl(n);
		   		writen(sock, &sendBuf, n + 4);
		    	memset(&sendBuf, 0, sizeof(sendBuf));
		    }
    	}
	}
    close(sock);
}

int main()
{
    int sock;
    if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    perror("socket error!");

    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(6000);
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
    {
    	perror("sockect error.");
    	exit(1);
    }

    doit(sock); 
    return 0;
}

//server.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "readwrite.h"

void do_srvce(int conn)
{
    int nready;
    fd_set rset;
    FD_ZERO(&rset);
    int fdstdin = fileno(stdin);
    int maxfd = conn > fdstdin ? conn:fdstdin;

    struct packet sendBuf;
    struct packet recvBuf;

    while(1)
    {
    	FD_SET(conn, &rset);
    	FD_SET(fdstdin, &rset);

    	nready = select(maxfd + 1, &rset, NULL, NULL, NULL);
    	if(nready == -1)
    	{
    		perror("select ret -1");
    		exit(-1);
   	 	}
   		else if(nready == 0)
    		continue;
    	if(FD_ISSET(conn, &rset))
    	{
   	 		memset(&recvBuf, 0, sizeof(recvBuf));
   			int ret = readn(conn, &recvBuf.len, 4);
    		if(ret == -1)
    		{
   				perror("conn ret -1");
    			exit(-1);
    		}
    		else if(ret < 4)
    		{
    			perror("client close.");
    			break;
    		}
    		int n = ntohl(recvBuf.len);
    		ret = readn(conn, recvBuf.content, n);
    		if(ret == -1)
    		{
    			perror("conn ret -1");
    			exit(-1);
    		}
    		else if(ret < n)
    		{
				perror("client close.");
				break;
    		}
    		fputs(recvBuf.content, stdout);
    	}
    	if(FD_ISSET(fdstdin, &rset))
    	{
    		if(fgets(sendBuf.content, sizeof(sendBuf.content), stdin) == NULL)
				break;
    		sendBuf.len = htonl(strlen(sendBuf.content));
    		writen(conn, &sendBuf, strlen(sendBuf.content) + 4);
    		memset(&sendBuf, 0, sizeof(sendBuf));
    	}
	}
    close(conn);
}

int main()
{
    int listenfd;
    if((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    perror("socket error!");

    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(6000);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
    	perror("bind error!");
    if(listen(listenfd, SOMAXCONN) < 0)
    	perror("listen");
    struct sockaddr_in peeraddr;
    socklen_t peerlen = sizeof(peeraddr);
    int conn;

    if((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)
    {
    	perror("accept error!");
    	exit(1); 
    }

    do_srvce(conn);
    close(listenfd);
    return 0;
}

select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述符就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回则设置timeout结构中的成员值为0),函数返回。当select函数返回后,可以通过遍历fdset,来找到就绪的描述符。

select的几大缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
(3)select支持的文件描述符数量太小了,默认是1024

pollfd结构包含了要监视的event和发生的revent,不再使用select“参数-值”传递的方式。同时,pollfd并没有最大数量限制(但是数量过大后性能也是会下降)。 和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符。

select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。

epoll解决了poll的三个缺点:

      对于第一个缺点,epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次。

  对于第二个缺点,epoll的解决方案不像select或poll一样每次都把current轮流加入fd对应的设备等待队列中,而只在epoll_ctl时把current挂一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表)。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd(利用schedule_timeout()实现睡一会,判断一会的效果,和select实现中的第7步是类似的)。

  对于第三个缺点,epoll没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。

版权声明:本文为博主原创文章,未经博主允许不得转载。

打字总结

 失败是什么?没有什么,只是更走近成功一步; 成功是什么?就是走过了所有通向失败的路,只剩下一条路,那就是成功的路。                                          ...
  • cjqhuadielei
  • cjqhuadielei
  • 2010年07月15日 13:46
  • 1360

伸展树(Splay tree)学习小结

转载请注明出处,谢谢 http://blog.csdn.net/ACM_cxlove?viewmode=contents           by---cxlove 总结一下最近学习的Splay...
  • just_sort
  • just_sort
  • 2016年08月25日 19:29
  • 735

MySql数据库基本select查询语句练习题,初学者易懂。

MySQL数据库的基本查询语句题目:
  • SoWhatWorld
  • SoWhatWorld
  • 2017年08月24日 10:14
  • 399

数据库操作练习1

查找最晚入职员工的所有信息 题目描述 查找最晚入职员工的所有信息 CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_dat...
  • sinat_22797429
  • sinat_22797429
  • 2017年08月19日 18:33
  • 319

T_SQL总结笔记

T_SQL总结笔记 一 基本语法 新建数据库 插入数据insert 修改数据记录Update 删除 1Delete记录日志 2清空删除Truncate不记录日志 3删除表drop表和日志都删除 二运算...
  • YLBF_DEV
  • YLBF_DEV
  • 2015年06月27日 17:10
  • 435

oracle之sql语句常见练习

--1列出至少有一个员工的所有部门。 SELECT dept.dname FROM dept WHERE deptno IN (SELECT deptno FROM emp GROUP BY dep...
  • chinabhlt
  • chinabhlt
  • 2015年03月20日 16:35
  • 477

常见sql语句练习

-- 教师表 CREATE TABLE teacher(     tno INT NOT NULL PRIMARY KEY,     tname VARCHAR(20) NOT NULL ); I...
  • Oliver_wq
  • Oliver_wq
  • 2017年10月28日 11:47
  • 1250

Radio、Checkbox、Select小结

Radio、Checkbox、Select
  • u013253310
  • u013253310
  • 2015年02月26日 17:07
  • 189

bootstrape select使用小结

看看上面的效果是bootstrape使用的效果。虽然不是很好看,但是符合bootstrape的风格。来看看普通的select的样式 bootstrape下的select和普通select在b...
  • u013132051
  • u013132051
  • 2017年03月18日 15:03
  • 10499

sql 语句 练习

sql 语句记忆简单的数据库语句练习 :select * from student where id=13 查出所有符合条件的记录select distinct name from student ...
  • chenaini119
  • chenaini119
  • 2016年06月28日 09:58
  • 376
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:select练习小结
举报原因:
原因补充:

(最多只允许输入30个字)