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))