转载出处:http://kenby.iteye.com/blog/1183579



步骤1: 设置非阻塞,启动连接

实现非阻塞 connect ,首先把 sockfd 设置成非阻塞的。这样调用connect 可以立刻返回,根据返回值和 errno 处理三种情况:

(1) 如果返回值 == 0,表示 connect 成功。

(2) 如果返回值 < 0, errno 为 EINPROGRESS, 表示连接建立已经启动但是尚未完成。这是期望的结果,不是真正的错误。

(3) 如果返回值 < 0,errno 不是 EINPROGRESS,则连接出错了。

步骤2:判断可读和可写

然后把 sockfd 加入 select 的读写监听集合,通过 select 判断 sockfd是否可写,处理三种情况:

(1) 如果连接建立好了,对方没有数据到达,那么 sockfd 是可写的。

(2) 如果在 select 之前,连接就建立好了,而且对方的数据已到达,那么 sockfd 是可读和可写的。

(3) 如果连接发生错误,sockfd 也是可读和可写的。

判断 connect 是否成功,就得区别 (2) 和 (3),这两种情况下 sockfd 都是可读和可写的,区分的方法是,调用 getsockopt检查是否出错。

步骤3:使用 getsockopt 函数检查错误

getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len)

在 sockfd 都是可读和可写的情况下,我们使用 getsockopt 来检查连接是否出错。但这里有一个可移植性的问题

如果发生错误,getsockopt 源自 Berkeley 的实现将在变量 error 中返回错误,getsockopt 本身返回0;然而 Solaris 却让 getsockopt 返回 -1,并把错误保存在 errno 变量中。所以在判断是否有错误的时候,要处理这两种情况。


c代码:

int conn_nonb(int sockfd, const struct sockaddr_in *saptr, socklen_t salen, int nsec)
{
int flags, n, error, code;
socklen_t len;
fd_set wset;
struct timeval tval;

flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

error = 0;
if ((n == connect(sockfd, saptr, salen)) == 0) {
goto done;
} else if (n < 0 && errno != EINPROGRESS){
return (-1);
}

/* Do whatever we want while the connect is taking place */

FD_ZERO(&wset);
FD_SET(sockfd, &wset);
tval.tv_sec = nsec;
tval.tv_usec = 0;

if ((n = select(sockfd+1, NULL, &wset,
NULL, nsec ? &tval : NULL)) == 0) {
close(sockfd); /* timeout */
errno = ETIMEDOUT;
return (-1);
}

if (FD_ISSET(sockfd, &wset)) {
len = sizeof(error);
code = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);
/* 如果发生错误,Solaris实现的getsockopt返回-1,
* 把pending error设置给errno. Berkeley实现的
* getsockopt返回0, pending error返回给error.
* 我们需要处理这两种情况 */
if (code < 0 || error) {
close(sockfd);
if (error)
errno = error;
return (-1);
}
} else {
fprintf(stderr, "select error: sockfd not set");
exit(0);
}

done:
fcntl(sockfd, F_SETFL, flags); /* restore file status flags */
return (0);
}

更多相关文章

  1. 铯:使用自己的OpenStreetMap服务器。:“未能获得图像块”错误
  2. OCaml编译错误:/ usr / bin / ld:找不到-lstr
  3. Linux 2.6.x 内核模块加载错误 “Invalid module format” 解决
  4. 解决useradd 用户后没有添加用户Home目录的情况,Linux改变文件或
  5. 在用apt源安装mysql-server时出现如下错误,需要安装mysql-server-
  6. 存储过程放在sql脚本里,命令行导入总是提示错误
  7. 解决缺少sql头文件编译错误
  8. “已有打开的与此命令相关联的 DataReader,必须首先将它关闭 ”错
  9. Mysql5.7.10版本安装后空密码登录,退出后提示密码错误连接不上解

随机推荐

  1. Android(安卓)判断当前介面是否是在桌面
  2. Android onMeasure、Measure、measureChi
  3. Android代碼執行shell 命令
  4. 问题小结(6)-listview滚动条相关
  5. Android 的网络编程(15)-Http JSon服务
  6. Android(安卓)淘气三千传之——Android搜
  7. Android中高级联动控件 RecyclerView+Vie
  8. [转] android 日期时间格式转换
  9. Android(安卓)TextView自动换行文字排版
  10. 2010.10.31———Android 04