1、在客户端服务器模式中,如果服务器退出,然后立即重新启动的话,然后就出现”试图绑定一个已经在使用的端口”的错误,要等过一段时间之后才可以bind,这是为什么呢???
或许你感到非常迷惑,明明服务器的套接字已经被关闭了,但为什么仍然禁止绑定端口。这是由于套接字处于TIME_WAIT状态引起的,这个状态会持续2MSL时间。在TIME_WAIT退出后,套接字被删除,该地址才能被重新绑定而不出现问题。
如图:
2、等待TIME_WAIT真的让人很不爽,可以采用下面这种方式避免TIME_WAIT状态。
在bind之前,用SO_REUSEADDR选项调用setsockopt函数。
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
功能:用于在任意类型、任意状态的套接字上设置选项值。选项影响套接字的操作。
参数:
sockfd:套接字的描述符。
level:选项定义的层次。
SOL_SOCKET: 基本套接口
IPPROTO_IP: IPv4套接口
IPPROTO_IPV6: IPv6套接口
IPPROTO_TCP: TCP套接口
optname:设置的选项。
optval:指向选项类型的指针。选项有两种类型的 ,一种是布尔类型的选项,表示允许(TRUE)或禁止(FALSE)一种特性。另一种整形选项,非零表示允许,零表示禁止。
optlen:optval缓冲区去的大小。
返回值:若无错误发生,则返回0。
选项 类型 意义
SO_BROADCAST BOOL 允许套接口传送广播信息。
SO_DEBUG BOOL 记录调试信息。
SO_DONTLINER BOOL 不要因为数据未发送就阻塞关闭操作。设置本选项相当于将SO_LINGER的l_onoff元素置为零。
SO_DONTROUTE BOOL 禁止选径;直接传送。
SO_KEEPALIVE BOOL 发送“保持活动”包。
SO_LINGER struct linger FAR* 如关闭时有未发送数据,则逗留。
SO_OOBINLINE BOOL 在常规数据流中接收带外数据。
SO_RCVBUF int 为接收确定缓冲区大小。
SO_REUSEADDR BOOL 允许套接口和一个已在使用中的地址捆绑(参见bind())。
SO_SNDBUF int 指定发送缓冲区大小。
TCP_NODELAY BOOL 禁止发送合并的Nagle算法。
setsockopt()不支持的BSD选项有:选项名 类型 意义
SO_ACCEPTCONN BOOL 套接口在监听。
SO_ERROR int 获取错误状态并清除。
SO_RCVLOWAT int 接收低级水印。
SO_RCVTIMEO int 接收超时。
SO_SNDLOWAT int 发送低级水印。
SO_SNDTIMEO int 发送超时。
SO_TYPE int 套接口类型。
IP_OPTIONS 在IP头中设置选项。
用法示例:
1.设置调用closesocket()后,仍可继续重用该socket。调用closesocket()一般不会立即关闭socket,而经历TIME_WAIT的过程。
BOOL bReuseaddr = TRUE;
setsockopt( s, SOL_SOCKET, SO_REUSEADDR, ( const char* )&bReuseaddr, sizeof( BOOL ) );
2.在send(),recv()过程中有时由于网络状况等原因,收发不能预期进行,可以设置收发时限:
int nNetTimeout = 1000; //1秒
//发送时限
setsockopt( socket, SOL_SOCKET, SO_SNDTIMEO, ( char * )&nNetTimeout, sizeof( int ) );
//接收时限
setsockopt( socket, SOL_SOCKET, SO_RCVTIMEO, ( char * )&nNetTimeout, sizeof( int ) );
例:编写一个基于TCP/IP的客户端-服务器模型,要求客户端能够向服务器发送数据,服务器收到后并显示出来。
//server.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
int startup(const char *ip,const char* port)
{
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
exit(1);
}
struct sockaddr_in serveraddr;
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(atoi(port));
serveraddr.sin_addr.s_addr=inet_addr(ip);
int on=1;
if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0)
{
perror("setsockopt");
exit(1);
}
if(bind(sock,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0)
{
perror("bind");
exit(1);
}
if(listen(sock,5)<0)
{
perror("listen");
exit(1);
}
return sock;
}
int main(int argc,char* argv[])
{
if(argc!=3)
{
printf("plese printf [ip] [port]");
return 3;
}
int lis_sock=startup(argv[1],argv[2]);
while(1)
{
socklen_t len=0;
struct sockaddr_in routme;
int connfd=accept(lis_sock,(struct sockaddr*)&routme,&len);
if(connfd<0)
{
continue;
}
printf("client link ip:%s port:%d\n ",\
inet_ntoa(ntohl(routme.sin_addr.s_addr)),ntohs(routme.sin_port));
char buf[1024];
while(1)
{
ssize_t s=read(connfd,buf,sizeof(buf)-1);
if(s<=0)
{
break;
}
buf[s]=0;
printf("client# %s\n",buf);
}
close(connfd);
}
return 0;
}
//client.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
int main(int argc,char *argv[])
{
if(argc!=3)
{
printf("printf [ip] [port]\n");
exit(1);
}
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
return -1;
}
struct sockaddr_in clientaddr;
clientaddr.sin_family=AF_INET;
clientaddr.sin_port=htons(atoi(argv[2]));
clientaddr.sin_addr.s_addr=inet_addr(argv[1]);
int connfd=connect(sock,(struct sockaddr*)&clientaddr,sizeof(clientaddr));
if(connfd<0)
{
perror("connet");
return -1;
}
printf("connect success...\n");
char buf[1024];
while(1)
{
printf("client# ");
fflush(stdout);
ssize_t s=read(0,buf,sizeof(buf)-1);
if(s>0)
{
write(sock,buf,s-1);
}
else
{
break;
}
}
close(sock);
return 0;
}