TCP检测连接非正常中断

http://blog.201314.info/tag/so_sndtimeo

在维护检测TCP的正常连接方面 SOL_SOCKET类提供了

SO_KEEPALIVE 保持连接 int
SO_RCVTIMEO 接收超时 struct timeval
SO_SNDTIMEO 发送超时 struct timeval

SOL_TCP类提供了

TCP_KEEPIDLE /*开始首次KeepAlive探测前的TCP空闭时间 */

TCP_KEEPINTVL  /* 两次KeepAlive探测间的时间间隔  */

TCP_KEEPCNT   /* 判定断开前的KeepAlive探测次数 */

上面的几个SOCKET属性是用来检测连接情况和传送接受属性的,但是在不停发包的情况下是不能使用的,不是说是TCP的缺陷,而是他设计的初衷就是为了维护短暂的中断的连接。 在已经连接上以后,不间断的发包会使得TCP的连接状态维持在ESTABLISHED上。

你可以在你的代码里面使用alarm函数,每隔几秒种发包,中间拔开网线然后用 netstat -t 来查看TCP的连接状况:

Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 192.168.10.22:5300      192.168.10.62:3117      ESTABLISHED

同样即使你发现网线拨开以后机子发送了几个ARP的查询包,很不幸的告诉你,即使没有收到ARP的查询回复包。在ARP Entry里面该mac地址然后不会被老化。

cat /proc/net/arp

IP address       HW type     Flags       HW address            Mask     Device
192.168.10.1     0×1         0×2         00:19:E0:DD:41:18     *        eth1
192.168.10.62    0×1         0×2         00:16:76:CC:1D:4E     *        eth1

如果你不想使用《UNIX 网络编程第1卷》上介绍的心博函数(因为需要客户端也要支持),你可以使用两个方法。这两种方法都属于非常规做法,希望各位在使用之前务必清楚TCP连接中会发生什么情况,返回什么样的错误号。

第一种:
Linux平台下,可以通过在connect之前设置SO_SNDTIMO来达到控制连接超时的目的。简单的写了份测试代码:


#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>

int main(int argc, char *argv[])
{
int fd;
struct sockaddr_in addr;
struct timeval timeo = {3, 0};
socklen_t len = sizeof(timeo);

fd = socket(AF_INET, SOCK_STREAM, 0);
if (argc == 4)
timeo.tv_sec = atoi(argv[3]);
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, len);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(argv[1]);
addr.sin_port = htons(atoi(argv[2]));
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
if (errno == EINPROGRESS) {
fprintf(stderr, "timeout/n");
return -1;
}
perror("connect");
return 0;
}
printf("connected/n");

return 0;
}

端口号使用原来连接的端口号去检测可以保证测试到对端的程序是否正常工作,使用广泛的端口号只能保证对端的主机是否存活。

第二种:
删除Arp地址,通过检测是否能够再次获得对端mac地址来判断对方是否存活。缺点是不能跨越路由器,同样不能检测可以保证测试到对端的程序是否正常工作:

#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/if_ether.h>

int get_arp(unsigned int ip)
{
int sd;

struct arpreq arpreq;
struct sockaddr_in *sin;
struct in_addr ina;
unsigned char *hw_addr;

int rc;

sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0)
{
perror(“socket() error/n”);
exit(1);
}

/* Try to find an entry in arp cache for the ip address specified */

printf(“Find arp entry for IP : %s/n”, ip);

memset(&arpreq, 0, sizeof(struct arpreq));
sin = (struct sockaddr_in *) &arpreq.arp_pa;
memset(sin, 0, sizeof(struct sockaddr_in));
sin->sin_family = AF_INET;
ina.s_addr = ip;//inet_addr(ip);
memcpy(&sin->sin_addr, (char *)&ina, sizeof(struct in_addr));

strcpy(arpreq.arp_dev, “eth1″);

rc = ioctl(sd, SIOCGARP, &arpreq);

return rc;
}
int del_arp(unsigned int ip)
{
int sd;

struct arpreq arpreq;
struct sockaddr_in *sin;
struct in_addr ina;
unsigned char *hw_addr;

int rc;

sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0)
{
perror(“socket() error/n”);
exit(1);
}

/* Try to find an entry in arp cache for the ip address specified */

printf(“Find arp entry for IP : %s/n”, ip);

memset(&arpreq, 0, sizeof(struct arpreq));
sin = (struct sockaddr_in *) &arpreq.arp_pa;
memset(sin, 0, sizeof(struct sockaddr_in));
sin->sin_family = AF_INET;
ina.s_addr = ip;//inet_addr(ip);
memcpy(&sin->sin_addr, (char *)&ina, sizeof(struct in_addr));

strcpy(arpreq.arp_dev, “eth1″);

rc = ioctl(sd, SIOCDARP, &arpreq);
return rc;
}

这样的方法比较简单,不要浪费资源。 用connect的在比较多的连接方面判断起来还是比较吃力的,但是它可以跨越路由器。

一般的情况在没有必要检测TCP的连接状态,主要是从TCP的机制考虑的,他的设计初衷就是为了保证间断的连接。但是在一些BT的系统中TCP系统实现的并不完善,需要这样辅助的手段保证对方重新使用新的端口重新连接。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值