解决关于gsoap生成的服务器关闭之后重启失败的问题

本文讲述了作者在使用GSoap生成web服务器时遇到的困难,包括如何解决套接字重用问题,通过深入理解工具内部结构,发现并修复了服务器关闭后无法重启的问题。最终,作者分享了修复过程和关键代码调整,帮助读者掌握如何控制和管理服务器生命周期。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一开始啥也不懂,查攻略查资料 通过很简单的几条语句 就通过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生成的服务器关闭之后重启失败的问题就已经解决了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值