linux下socket常见问题整理
1. socket不能立即关闭的问题
每次修改了源代码并再次编译运行时,常遇到下面的错误:
Cann't bind server socket !
Address already in use
解决方法为使用setsockopt函数设置SO_REUSEADDR,示例代码如下:
int reuse = 0;
struct sockaddr_in cliaddr, servaddr;
listenfd = socket(PF_INET, SOCK_STREAM,0);
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
perror("setsockopet error\n");
return -1;
}
2. 关于tcp客户端非正常掉线的问题
开启探测属性,在一段时间内没有数据往来,则发送探测包,若探测包收得不到响应,则重发一定次数,若还未得到响应则认为链路断开,示例代码如下:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
int keepAlive = 1; // 开启keepalive属性
int keepIdle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测
int keepInterval = 5; // 探测时发包的时间间隔为5 秒
int keepCount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.
setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));
setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));
setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));
setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));
ioctl(s, FIONBIO, &non_blocking);/*设置非堵塞*/
3. socket客户端关闭检测
方式1:当使用 select()函数测试一个socket是否可读时,如果select()函数返回值为1,且使用recv()函数读取的数据长度为0 时,就说明该socket已经断开,示例代码:
if(FD_ISSET(net->desfd,&tempset)) {
char recvbuf[1024] = {0};
int ret = read(net->desfd, recvbuf, 1024);
if (ret == -1)
ERR_EXIT("readline error");
else if (ret == 0) { //客户端关闭
printf("client %d close \n",net->desfd);
FD_CLR(net->desfd, &mset);
client[i] = -1;
close(net->desfd);
}
}
方式2:用getsockopt来判断,示例代码:
#include <linux/types.h>
#include <asm/byteorder.h>
#include <linux/config.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <net/sock.h>
int SocketConnected(int sock)
{
if (sock <= 0)
return 0;
struct tcp_info info;
int len = sizeof(info);
getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *) & len);
if ((info.tcpi_state == TCP_ESTABLISHED)) {
//myprintf("socket connected\n");
return 1;
} else {
//myprintf("socket disconnected\n");
return 0;
}
}
4. 关于指定客户端ip的问题
对于服务器来说,设置指定ip地址的访问,主要是通过accept函数返回的客户端的ip和端口号来判断,若不是所设定的则进行关闭操作。
客户端在连接时,也可指定自己的ip地址(同服务器),在connet连接之前通过bind绑定。
cln->srcaddr.sin_family = AF_INET;
cln->srcaddr.sin_port = htons(5555);
cln->srcaddr.sin_addr.s_addr = inet_addr("192.166.0.222");
bind(cln->srcfd, (struct sockaddr *)&(cln->srcaddr), sizeof(cln->srcaddr))