在学习嵌入式Linux网络编程中,很多同学都发现了一个问题,那就是调用connect函数时,如果服务端关闭,客户 端调用connect()函数时,发现阻塞在那里,而且利用ctrl+c信号去停止客户端程序时,需要等待一个较为长的时间才能响应了,这个时间如果大家 细心会发现,每次都是75秒的时间。那么有没有什么比较好的办法,可以以用户能接受的一个时间响应来停止掉一个正在connect连接的客户端那?
---------------------
作者:hj605635529
来源:CSDN
原文:https://blog.csdn.net/hj605635529/article/details/74157305
版权声明:本文为博主原创文章,转载请附上博文链接!
步骤1: 设置非阻塞,启动连接
实现非阻塞 connect ,首先把 sockfd 设置成非阻塞的。这样调用
connect 可以立刻返回,根据返回值和 errno 处理三种情况:
(1) 如果返回 0,表示 connect 成功。
(2) 如果返回值小于 0, errno 为 EINPROGRESS, ?表示连接
? ? ? 建立已经启动但是尚未完成。这是期望的结果,不是真正的错误。
(3) 如果返回值小于0,errno 不是 EINPROGRESS,则连接出错了。
?
步骤2:判断可读和可写
然后把 sockfd 加入 select 的读写监听集合,通过 select 判断 sockfd
是否可写,
(1) 如果连接建立好了,,那么 sockfd 是可写的
(2) 如果连接发生错误,sockfd 也是可读和可写的。
步骤3:使用 getsockopt 函数检查错误
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, &len)
在 sockfd 都是可读和可写的情况下,我们使用 getsockopt 来检查连接
是否出错。但这里有一个可移植性的问题。
如果发生错误,getsockopt 源自 Berkeley 的实现将在变量 err 中
返回错误,getsockopt 本身返回0;(centos是这种错误)
然而 Solaris 却让 getsockopt 返回 -1,
并把错误保存在 errno 变量中。所以在判断是否有错误的时候,要处理
这两种情况。
步骤4:重新将套接字设置为阻塞
#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<string.h>
#include<netinet/in.h>
#include<string.h>
#include <sys/select.h>
#include<fcntl.h>
/* According to earlier standards */
#include <sys/time.h>
#include <unistd.h>
#include<errno.h>
static void usage(const char *str)
{
printf("Usage:%s,[IP],[PORT]\n",str);
}
int main(int argc ,char *argv[])
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0)
{
perror("sock");
return 1;
}
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(atoi(argv[2]));
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
int flag,old_flag;
flag |= O_NONBLOCK;
old_flag = flag = fcntl(sock, F_SETFL, O_NONBLOCK ); //将连接套接字设置为非阻塞。
int ret = connect(sock,(struct sockaddr*)& serv_addr,sizeof(serv_addr));
if(ret != 0)
{
if(errno != EINPROGRESS) //connect返回错误。
{
printf("connect failed\n");
}
//连接正在建立
else
{
struct timeval tm; //1.定时
tm.tv_sec = 10;
tm.tv_usec = 0;
fd_set wset;
FD_ZERO(&wset);
FD_SET(sock,&wset);
printf("selcet start\n");
int res = select(sock+1, NULL, &wset, NULL, &tm);
printf("select end\n");
if(res <= 0)
{
printf("res <= 0\n");
close(sock);
return 2;
}
if(FD_ISSET(sock,&wset))
{
printf("test \n");
int err = -1;
socklen_t len = sizeof(int);
if(getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len ) < 0) //两种错误处理方式
{
printf("errno :%d %s\n",errno, strerror(errno));
close(sock);
return 3;
}
if(err)
{
printf("connect faile\n");
errno = err;
close(sock);
return 4;
}
printf("connetct success\n");
}
}
}
fcntl(sock, F_SETFL, old_flag); //最后恢复sock的阻塞属性。
char buf[BUFSIZ];
while(1)
{
printf("client say# ");
fflush(stdout);
ssize_t s = read(0,buf,sizeof(buf) - 1);
if(s > 0)
{
buf[s - 1] = 0;
write(sock,buf,strlen(buf));
ssize_t s2 = read(sock,buf,sizeof(buf) -1 );
if(s2 > 0)
{
buf[s2] = 0;
printf("server echo # %s\n",buf);
}
}
}
return 0;