sokcet常用配置

非阻塞模式

套接字有两种工作模式:

  • 阻塞模式: 阻塞调用是指调用结果返回之前,当前线程会被挂起。该进程被标记为睡眠状态并被调度出去。函数只有在得到结果之后才会返回。当socket工作在阻塞模式的时候, 如果没有数据的情况下调用该函数,则当前线程就会被挂起,直到有数据为止。
  • 非阻塞模式: 非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。epoll工作在非阻塞模式时,才会发挥作用。

正常情况下,socket工作在阻塞模式下,在调用accept,connect,read,write等函数时,都是阻塞方式,直到读到数据才会返回。但是,如果将socket设置为非阻塞状态,那么这么些函数就会立即返回,不会阻塞当前线程。
设置非阻塞socket的方法是:

int flags = fcntl(app_socket->fd, F_GETFL, 0);
flags |= O_NONBLOCK;
flags |= O_NDELAY;
fcntl(app_socket->fd, F_SETFL, flags);

建立连接

客户端调用 connect() 发起对服务端的 socket 连接,
如果客户端的 socket 描述符为阻塞模式,则 connect() 会阻塞到连接建立成功或连接建立超时
(Linux 内核中对 connect 的超时时间限制是 75s, Soliris 9 是几分钟,因此通常认为是 75s 到几分钟不等)。

如果为非阻塞模式,则调用connect()后函数立即返回,如果连接不能马上建立成功(返回 -1),则 errno 设置为 EINPROGRESS,此时 TCP 三次握手仍在继续。此时可以调用 select() 检测非阻塞 connect 是否完成。select 指定的超时时间可以比 connect 的超时时间短,因此可以防止连接线程长时间阻塞在 connect 处。

处理非阻塞 connect 的步骤:

  1. 创建socket,并利用fcntl将其设置为非阻塞。
  2. 调用connect函数,如果返回0,则连接建立;如果返回-1,检查errno ,如果值为 EINPROGRESS,则连接正在建立。
  3. 为了控制连接建立时间,将该socket描述符加入到select的可写集合中,采用select函数设定超时。
  4. 如果规定时间内成功建立,则描述符变为可写;否则,采用getsockopt函数捕获错误信息。当errno == 0表示只可写。

源自Berkeley的实现(和Posix.1g)有两条与select和非阻塞IO相关的规则:
A: 当连接建立成功时,套接字描述符变成可写;
B: 当连接出错时,套接字描述符变成既可读又可写;

所以对于 非阻塞模式connect() 的判断逻辑如下:

 1. connect()返回值为0,连接成功。
 2. connect()返回值不为0,且 errno 的值也不为EINPROGRESS,表示连接失败。
 3. connect()返回值不为0,但是 errno 为EINPROGRESS,则使用 select 进行下一步判断。
 	3.1 如果 select() 返回 0,表示在 select() 超时时间内未能成功建立连接。
 	3.2 如果 select() 小于 0,表示连接失败。
 	3.3 如果 select() 大于 0,表示检测到存在变化套接字描述符。
 		3.3.1 如果套接字描述符不可写,则连接失败。
 		3.3.2 如果套接字描述符变成可写,连接成功。
 		3.3.3 如果套接字描述符变成可读可写,采用getsockopt函数捕获错误信息。当errno == 0表示只可写,连接成功,否则连接失败。
 		3.3.4 [可选]可以再次进行connect()连接,如果返回值为EISCONN,则认为已经连接

断开连接

当select检测到套接字描述符可写,但是接收到数据为0时,认为是断开连接。

if(select(fd_max + 1, &read_set, NULL, NULL, &time)>0) {
	if(FD_ISSET(app_socket->fd, &read_set) ){
		recv_len = read(app_socket->fd, buff, 100);
		if (recv_len == 0){
            // Socket 断开
        }
        else{
            int ret = lwip_get_error(app_socket->fd);
            if (ret != EAGAIN && ret != EINTR){
                // Socket 断开
            }
        }
    }
}

配置

int reuseaddr = 1; // 允许重用本地地址和端口
setsockopt(app_socket->fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&reuseaddr, sizeof(reuseaddr));

int keepalive = 1;     // 开启keepalive属性
int keepidle = 60;     // 如果连接过程中20s没有数据就发送一个探测包
int keepcount = 3;     // 一共发送探测包次数,期间收到回复就不再发送下一次的探测包
int keepinterval = 10; // 探测包的时间间隔为10s
setsockopt(app_socket->fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof(keepalive));
setsockopt(app_socket->fd, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&keepidle, sizeof(keepidle));
setsockopt(app_socket->fd, IPPROTO_TCP, TCP_KEEPCNT, (void *)&keepcount, sizeof(keepcount));
setsockopt(app_socket->fd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&keepinterval, sizeof(keepinterval));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

盗版摩羯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值