一开始啥也不懂,查攻略查资料 通过很简单的几条语句 就通过gsoap生成了webserver服务器,具体流程可以看这里。
但之后要求能够自己关闭服务器,又可以自己开启服务器,怎么办呢?
源代码只有循环结束之后的这两句
m_pService->reset();
m_pService->destroy();
我首先的想法就是加一个关闭服务器接口,再加一个标志位,这个标志位用于break循环,接口如下
int WebServerRealServer::StopServer()
{
m_bStop = true;
m_pService->reset();
m_pService->destroy();
return 1;
}
很显然,这样肯定不行,通过一边调试一边修改代码,我主要得到了以下几种错误代码
GetLastError()返回
(10048)-通常每个套接字地址 (协议/网络地址/端口)只允许使用一次。
(10013)-以一种访问权限不允许的方式做了一个访问套接字的尝试。
(10038)-在一个非套接字上尝试了一个操作。
当我开始针对这几种错误一个一个解决的同时,我发现这没什么效果。
我意识到我对于gsoap这个工具的了解实在是太少了,于是我开始看它的源码,看socket编程的知识,刚好我发现了里面的一些有趣的地方。
struct SOAP_CMAC soap
{
int socket_flags; /* user-definable socket recv() and send() flags, e.g. set to MSG_NOSIGNAL to disable sigpipe */
int connect_flags; /* user-definable connect() SOL_SOCKET sockopt flags, e.g. set to SO_DEBUG to debug socket */
int connect_retry; /* number of times to retry connecting (exponential backoff), zero by default */
int bind_flags; /* user-definable bind() SOL_SOCKET sockopt flags, e.g. set to SO_REUSEADDR to enable reuse */
short bind_inet6; /* user-definable, when > 0 use AF_INET6 instead of PF_UNSPEC (only with -DWITH_IPV6) */
short bind_v6only; /* user-definable, when > 0 use IPPROTO_IPV6 sockopt IPV6_V6ONLY (only with -DWITH_IPV6) */
int accept_flags; /* user-definable accept() SOL_SOCKET sockopt flags */
SOAP_SOCKET master; /* socket bound to TCP/IP port */
SOAP_SOCKET socket; /* socket to send and receive */
SOAP_SOCKET sendsk; /* socket to send (overrides ::socket) */
SOAP_SOCKET recvsk; /* socket to receive (overrides ::socket) */
}
我发现soap里居然使用到了4个socket,一个监听套接字用于监听端口,一个收发套接字,一个收套接字和一个发套接字,有意思的是它的接口soap_force_close_socket()仅仅只关闭了其中的一个收发socket,也就是说它的套接字没有全部关闭!
问题解决:
于是我修改了这个接口如下,然后在我新增加的StopServer()接口中调用
SOAP_FMAC1
int
SOAP_FMAC2
soap_force_closesock(struct soap *soap)
{
////添加
if (soap_valid_socket(soap->master) && soap->fclosesocket)
{
(void)soap->fclosesocket(soap, soap->master);
soap->master = SOAP_INVALID_SOCKET;
}
soap->keep_alive = 0;
if (soap_valid_socket(soap->socket) && soap->fclosesocket)
{
(void)soap->fclosesocket(soap, soap->socket);
soap->socket = SOAP_INVALID_SOCKET;
}
//////添加
if (soap_valid_socket(soap->sendsk) && soap->fclosesocket)
{
(void)soap->fclosesocket(soap, soap->sendsk);
soap->sendsk = SOAP_INVALID_SOCKET;
}
if (soap_valid_socket(soap->recvsk) && soap->fclosesocket)
{
(void)soap->fclosesocket(soap, soap->recvsk);
soap->recvsk = SOAP_INVALID_SOCKET;
}
//////
return soap->error;
}
然后我在bind之前设置
Socket中SO_REUSEADDR详解
其实还有一个SO_REUSEPORT也可以,但Windows仅有SO_REUSEADDR选项
m_Service.bind_flags += SO_REUSEADDR;//绑定时立即重用端口
m_Service.accept_flags += SO_REUSEADDR;
m_Service.connect_flags += SO_REUSEADDR;
最后就是设置bind时的第三个参数backlog=1,这个调用bind时传进去就行
if (!soap_valid_socket(m_Service.bind(m_strIpAddress.c_str(), m_nPort, 1)))
至此关于gsoap生成的服务器关闭之后重启失败的问题就已经解决了。