Chinaunix首页 | 论坛 | 博客
  • 博客访问: 275109
  • 博文数量: 89
  • 博客积分: 1380
  • 博客等级: 中尉
  • 技术积分: 705
  • 用 户 组: 普通用户
  • 注册时间: 2009-07-10 11:04
文章分类

全部博文(89)

文章存档

2014年(4)

2011年(1)

2010年(42)

2009年(42)

我的朋友

分类: 系统运维

2010-03-11 10:17:14

再次分析非阻塞性sock的读取

read()返回值有三:
1. 大于0(简单)
   继续读

2. 等于0(简单)
   对方关闭,自己也需要返回错误,然后关闭。

3. 小于0()
   小于零的分析见下面的一些摘录。

 


1.
这个程序在多个linux服务器上运行,都没有问题,但最近在一台Red   Hat   Linux   release   8.0   (Psyche)   Kernel   2.4.18-14   上总是出现通过select调用read()时返回值-1,errno值为11(EWOULDBLOCK),
出错信息是Resource   temporarily   unavailable。  
  出现该错误,如果多次重读,偶尔会出现读取到异常包的现象。  
  请问这是什么原因,是不是和操作系统有关?
2.
4 楼x86(大雪)
回复于 2005-11-08 10:27:08 得分 0

EAGAIN=EWOULDBLOCK(BSD风格)  
   
  此错误由在非阻塞套接字上不能立即完成的操作返回,例如,当套接字上没有排队数据可读时调用了recv()函数。此错误不是严重错误,相应操作应该稍后重试。对于在非阻塞   SOCK_STREAM套接字上调用connect()函数来说,报告EWOULDBLOCK是正常的,因为建立一个连接必须花费一些时间。
  
3.
7 楼do_do(do_do)
回复于 2005-11-08 15:22:59 得分 0

大雪是对的,EWOULDBLOCK的意思是如果你不把socket设成非阻塞(即阻塞)模式时,这个读操作将阻塞,也就是说数据还未准备好(但系统知道数据来了,所以select告诉你那个socket可读)。使用非阻塞模式做I/O操作的细心的人会检查errno是不是EAGAIN、EWOULDBLOCK、EINTR,如果是就应该重读,一般是用循环。如果你不是一定要用非阻塞就不要设成这样,这就是为什么系统的默认模式是阻塞。 

4.
在Linux进行非阻塞的socket接收数据时经常出现Resource temporarily unavailable,errno代码为11(EAGAIN),这是什么意思?

  这表明你在非阻塞模式下调用了阻塞操作,在该操作没有完成就返回这个错误,这个错误不会破坏socket的同步,不用管它,下次循环接着recv就可以。对非阻塞socket而言,EAGAIN不是一种错误。在VxWorks和Windows上,EAGAIN的名字叫做EWOULDBLOCK。

另外,如果出现EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。

  最后,如果recv的返回值为0,那表明连接已经断开,我们的接收操作也应该结束

 

 

 

 

 

10.
宏: int EAGAIN
    资源暂时不可用。你再次调用函数有可能成功。宏EWOULDBLOCK是
EAGAIN的另一个名字。它们在GNU库中总是相同的。
    这个错还有可能在一些其他情况发生。
    *对非阻塞模式的对象进行某个会阻塞的操作。再次做同样的操作
就会阻塞直到某种条件使它可以读,写,连(不管是什么操作)。你可以使用
selece来找出哪些操作是可能的,参见等待输入输出(Waiting for Input or
Output).移植性考虑,在许多老的Unix系统上,这种错误被指示为与EAGAIN
不同的EWOULDBLOCK。为了便于移植,你可以同时检验两种错误并做同样处理。
    *某个资源上的故障使操作不能进行。例如fork有可能返回着个错误,
这也表示这个故障可以被克服,所以你的程序可以以后尝试这个操作。停几秒
让其他进程释放资源然后再试也许是个好主意。这种故障可能很严重并会影响
整个系统,所以通常交互式的程序会报告用户这个错误并返回命令循环。

宏: int EWOULDBLOCK
    对 GNU C库来说,这只是EAGAIN的另一个名字,在每一个操作系统上
这两个值都是一样的。
    有些老的UNIX系统上这两个值不一样。

宏: int EINPROGRESS
    对某个设置成非阻塞模式的对象的操作不能马上结束。某些函数可能
永远是阻塞的(例如connect, 参见建立连接Making a connection),它们不会
返回EAGAIN。它们返回EINPROGRESS表示操作已经开始并需要一些时间来完成。
在操作完成以前对对象的操作会返回EALREADY。你可以使用select来检测什么时
候等待的操作被完成。参见等待输入输出(Waiting for Input or Output).

 

11.
#define SERV_PORT    5000
#define MAXLINE        1000

 


/*从sock读N个字节,成功返回n>=0 不成功-1*/
static ssize_t _tcp_readn(int fd, void *vptr, size_t n)
{
    size_t nleft;
    ssize_t nread;
    char *ptr;


    ptr = (char*)vptr;
    nleft = n;
    while (nleft > 0)
    {
        nread = read(fd, ptr, nleft);
        if (nread < 0)
        {
            if (errno == EINTR)
            {
                continue;
            }
            else if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                continue;
            }
            else
            {
                return  - 1;
            }
        }
        else if (nread == 0)
        {
            //closed
            break;
        }

        nleft -= nread;
        ptr += nread;
    }

    return (n - nleft);
}

/*写sock N个字节,成功返回n>=0 不成功-1*/
static ssize_t _tcp_writen(int fd, const void *vptr, size_t n)
{
    size_t nleft;
    ssize_t nwritten;
    const char *ptr;

    ptr = (char*)vptr;
    nleft = n;
    while (nleft > 0)
    {
        nwritten = write(fd, ptr, nleft);
        if (nwritten <= 0)
        {
            if (errno == EINTR)
            {
                continue;
            }
            else if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                continue;
            }
            else
            {
                return  - 1;
            }
        }

        nleft -= nwritten;
        ptr += nwritten;
    }

    return n;
}

11.
关于EINTR的问题

我想问的是,,什么时候会产生这个EINTR信号呢?我的程序代码里并没有产生该信号。
我以前碰到一个程序,跟这个结构差不多,,是一个while循环,总是报错说,产生了EINTR信号。所以

我一直不明白,,到底是什么产生了这个信号???请大家赐教。

回答:
自己系统中,可能非有意而为产生信号的原因:

进程调试
控制台切换
任务切换
伪终端断开
异步IO
父进程死亡导致孤儿进程
子进程退出
系统重启或关闭

只要保证你的程序不受上述原因影响可能就不需要再检查信号打断错误了。
 
等等,这些事件发生,会产生打断(打扰)EINTR信号,忽略继续处理就可以了。

感悟:
read(或者write)时,应从对方Ewouldblock/Eagain,还有自己EINTer信号,
两个角度考虑。
有整体性的逻辑。

 

为什么会有Ewouldblock/Eagain,上面的某个已经说明了。好像是默认是阻塞的,该
非阻塞时,调用阻塞函数而又没数据时,等等。


15.
如何设置socket的Connect超时(linux)

1.首先将标志位设为Non-blocking模式,准备在非阻塞模式下调用connect函数
2.调用connect,正常情况下,因为TCP三次握手需要一些时间;而非阻塞调用只要不能立即完成就会返回错误,所以这里会返回EINPROGRESS,表示在建立连接但还没有完成。

已经说得很明白了,判断EINprocess错误。
man:
If the connection cannot be established  immediately  and  O_NONBLOCK  is  set  for  the  file
       descriptor  for  the socket, connect() shall fail and set errno to [EINPROGRESS], but the con-
       nection request shall not be aborted, and the connection shall be established  asynchronously.

阅读(5313) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~