现在的位置: 首页 > 综合 > 正文

UNIX网络编程(二)

2013年12月08日 ⁄ 综合 ⁄ 共 3396字 ⁄ 字号 评论关闭

下面继续分别介绍TCP服务器和客户端建立连接的几个重要函数以及相应错误处理

3.地址绑定

       int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

       当套接字被创建后 它存在于一个地址族的名字空间里 但是没有被赋予一个地址 bind的作用就是给指定的套接字绑定地址

       客户端和服务器端都可以调用此函数来绑定地址 客户端如果不绑定 系统会随机分配一个网络出口 服务器在accept之前通常是需要绑定地址的

       参数没啥可说的

       返回值:0为成功 -1为错误 errno被设置

       常见错误:EACCESS 地址被保护 而且此用户不是root

                      EADDRINUSE 给定的地址已经被使用

                      EBADF 此套接字不是一个有效的描述符

                      EINVAL 此套接字已经被绑定

                      ENOTSOCK 此描述符不是个套接字 而是指向一个文件

4.监听

       int listen(int sockfd, int backlog);

       此操作也是服务器接收连接前必须要做的 即把一个套接字设置为被动的监听套接字

       backlog:这个参数恐怕是需要重点说明的 系统内核会在调用此函数后维护backlog长度的队列 其中分为两类元素 一类是半连接

                    一类是已经建立的连接 客户端发个SYN包过来 ACK和SYN包回去 而还没收到最后一个ACK 此时此套接字即处于半连接状态

                    若3次握手全部完毕则为全连接状态 OS做这些都是完全异步的 应用层不可控制 一般此队列长度设置为一个经验值 5 8 20

       返回值:0为成功 -1为错误 errno被设置

       常见错误:EADDRINUSE 另一个套接字已经监听在同一端口

                      EBADF 不是一个有效的描述符

                      ENOTSOCK 此描述符不是个套接字

                      EOPNOTSUPP 此套接字不支持监听操作

5.连接

       int connect(int sockfd, const struct sockaddr *serv_addr. socklen_t addrlen);

       客户端可以通过此操作来建立连接 内部的操作就是发一个SYN包 值得一提的是 不论TCP或是UDP都可以调用此函数

       TCP不用说 就是连接到与服务器地址绑定的套接字

       UDP如果调用此函数 则指定了默认的发送和接收地址 收发函数也要相应的换为有连接版本(send recv等)

       还有一点变化就是已连接套接字可以返回异步错误 而未连接套接字不返回任何异步错误

       参数无需赘言

       返回值:0为成功 -1为错误 errno被设置

       常见错误:EACCESS 只对UNIX域套接字 如果对文件没有写权限或对目录没有执行权限

                      EADDRINUSE 本地地址已经被使用

                      EAFNOSUPPORT 地址格式错误

                      EAGAIN 没有更多自由的本地端口或走了路由表中无效的入口

                      EALREADY 套接字为非阻塞而且前一个连接尝试还没有完成

                      EBADF 文件描述符无效

                      ECONNREFUSED 远程地址没有监听 即连接被拒绝

                      EFAULT 套接字结构体地址在用户的地址空间之外

                      EINPROGRESS 套接字为非阻塞而且连接无法马上建立完成

                      EINTR 此系统调用被信号中断

                      EISCONN 此套接字已建立连接

                      ENETUNREACH 网络不可达

                      ENOTSOCK 此描述符不是个套接字

                      ETIMEOUT 尝试建立连接时超时 服务器可能太忙而不能接收新连接

        建立连接是一个复杂的过程 所以错误种类很多 其中需要重点关注的有:

        EAGAIN:这是个在非阻塞收发数据时很常见的错误 但在connect中好像比较少出现 上面说过 客户端不调用bind时

                     是由OS来做本地地址绑定的 所以可能出现这样的错误

        EALREADY EINPROGRESS EISCONN ETIMEOUT:常见错误 都很重要 需要记得牢靠

        EINTR:这也就是connect可以用信号(比如sigalarm)来控制超时的原因 阻塞的系统调用在被信号中断时都会产生此错误

5.接受连接

       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

       整个连接建立成功只剩下这一个系统调用

       如上listen中所述 accept会从已经完成连接的队列里拿出一个新套接字 作为返回值

       如果为阻塞调用 accept会一直阻塞到出现一个新连接套接字 否则 非阻塞调用而且新连接队列为空的话 返回失败 并设置EAGAIN

       可以通过select或epoll等多路复用策略来对多个套接字进行处理 只要检测到监听套接字上的可读事件 则调用accept接受新连接

       但是 要注意 每次accept只返回一个套接字(好像是废话) 状态为可写(因为初始的发送缓冲区为空) 

       所以当队列中有多个已连接套接字时 很可能是没有办法马上处理的

       这样造成的后果就是: 有可能返回的套接字已经接收了客户端的数据 状态为既可读又可写

                                       还有一种可能 accept查看队列发现有套接字就绪 就在取出这个套接字前的间歇 客户端突然发了一个RST过来

                                       导致连接关闭 accept则永远阻塞 也不会再接受其他已连接套接字了

       还是说一下参数 第一个为监听套接字 后面两个是客户端的地址信息 可以都写为NULL来表示不关心客户信息 不必填写

                              第三个参数是值-结果参数 调用前需存入sockaddr的大小 返回时被填入的真正大小 两者可能不同 每次调用须重置

       返回值 成功则非负套接字 失败则-1 设置errno

       常见错误:EAGAIN或EWOULDBLOCK 套接字被设置为非阻塞 而且当前没有新连接的套接字

                      EBADF 无效的描述符

                      ECONNABORTED 连接被中断

                      EINTR 在有连接到来之前系统调用被信号中断

                      EINVAL 套接字没有监听连接 或长度无效(比如为负)

                      EMFILE 达到进程的打开文件描述符总数上限

                      ENFILE 达到系统的打开文件描述符总数上限

                      ENOTSOCK 描述符不是套接字

                      EOPNOTSUPP 此套接字不是SOCK_STREAM类型 无连接

                      EFAULT 参数addr不可写

        注意 得到的新连接套接字的文件状态(O_NONBLOCK O_ASYNC等)是否继承监听套接字 是平台相关的 如果编写可移植程序 不能依赖此特性 而应重新对其进行设置

抱歉!评论已关闭.