bind failed in soap_bind() 问题解决

使用gsoap协议的时候,如果服务异常退出,再次打开服务的时候,就会出现“bind failed in soap_bind()”的错误,如下:
[billing_dx@bmcs1 test]$addserver 9999
SOAP 1.1 fault: SOAP-ENV:Server [no subcode]
"Address already in use"
Detail: bind failed in soap_bind()
想让这个异常重现的方法很简单,启动服务端之后,再启动客户端,一旦客户端和服务端进行交互之后,立马使用“Ctrl+C”强制退出服务,然后立马重新服务,就会问题重现。
虽然等一段时间就可以重新使用该端口,但是,有没有办法避免这种情况呢?
使用sock的时候,我们其实也会遇到同样的问题,就是服务端异常退出的时候,就会出现“bind: Address already in use”的错误。而解决办法就是使用“setsockopt”函数,使用方式如下:
int optval=1;
if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval))<0)
{
	perror("Failed to set address reuse.\n");
	exit(1);
}
if((bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr)))==-1)
{
	perror("bind");
	exit(1);
}
这是在bind函数之前,这样的话,即使服务器端异常退出,也不会出现绑定错误的提示了。
后来上网搜了一下setsockopt的用法,发现他是用来设置socket的状态,当然,它的使用方法很多。对于上面的用法,我说一下我自己的理解:
当我们在关闭socket的时候,socket一般不会立即关闭,而是经历了一个TIME_WAIT的过程,我们在异常退出socket的时候,也是如此。因此,如果我们异常退出之后再重新使用socket话,就会因为上面的时间延迟导致端口已经被绑定。而这个函数的作用,就是让socket不再延时,而是直接退出,这样我们就可以使用原来的socket而不会发生bind错误了。
在gsoap中,我们通过修改gsoap提供的“stdsoap2.c”文件,就可以达到目的。
通过搜索出现的错误信息“bind failed in soap_bind()”,找到了对应的文件是“stdsoap2.c”,对应的文件内容如下:
if (bind(soap->master, (struct sockaddr*)&soap->peer, (int)soap->peerlen))
{
	soap->errnum = soap_socket_errno(soap->master);
	DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not bind to host\n"));
	soap_closesock(soap);
	soap_set_receiver_error(soap, tcp_error(soap), "bind failed in soap_bind()", SOAP_TCP_ERROR);
	return SOAP_INVALID_SOCKET;
}
在这之前,添加“setsockopt”函数,使用同样的异常处理就可以了,如下:
int optval=1;
if (setsockopt(soap->master,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval)) < 0)
{
	soap->errnum = soap_socket_errno(soap->master);
	DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not bind to host\n"));
	soap_closesock(soap);
	soap_set_receiver_error(soap, tcp_error(soap), "bind failed in soap_bind()", SOAP_TCP_ERROR);
	return SOAP_INVALID_SOCKET;
}
if (bind(soap->master, (struct sockaddr*)&soap->peer, (int)soap->peerlen))
{
	soap->errnum = soap_socket_errno(soap->master);
	DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not bind to host\n"));
	soap_closesock(soap);
	soap_set_receiver_error(soap, tcp_error(soap), "bind failed in soap_bind()", SOAP_TCP_ERROR);
	return SOAP_INVALID_SOCKET;
}
进行同样的服务端异常退出再登陆,OK,不会出现端口号绑定的情况了
代码优化下面的代码:DWORD WINAPI SoapServerThread( LPVOID pParam ) { CUserManagerServerDlg *pUserManServer = (CUserManagerServerDlg *)pParam; struct soap Soap_service; soap_init(&Soap_service); Soap_service.pUserManagerServerDlg = pUserManServer; Soap_service.bind_flags = SO_REUSEADDR; USES_CONVERSION; std::string strIP(W2A(pUserManServer->m_csIP)); const char* chIP = strIP.c_str(); CString csInfo; int nBindErr; if ((nBindErr = soap_bind(&Soap_service,chIP, pUserManServer->m_nPort, 100))< 0) //soap_bind(&math_service, address, port, 100); { soap_print_fault(&Soap_service, stderr); //csInfo.Format(_T("用户服务器绑定失败")); csInfo.LoadString(IDS_BIND_FAILD); pUserManServer->RecordOperaInfo(csInfo,6); pUserManServer->RecordSQL(csInfo,6); return -1; } if (pUserManServer->m_csIP.IsEmpty() || !pUserManServer->m_nPort) { //csInfo.Format(_T("IP和端口号不允许为空!")); csInfo.LoadString(IDS_CANNOT_EMPTY); pUserManServer->RecordOperaInfo(csInfo,6); pUserManServer->RecordSQL(csInfo,6); return -1; } //csInfo.Format(_T("用户服务器已启动...")); csInfo.LoadString(IDS_STARTED); pUserManServer->RecordOperaInfo(csInfo,0); pUserManServer->RecordSQL(csInfo,0); CString csListen; csListen.LoadString(IDS_LISTEN); csInfo.Format(_T("%s:IP(%s),Port:(%d)"),csListen,pUserManServer->m_csIP,pUserManServer->m_nPort); pUserManServer->RecordOperaInfo(csInfo,0); pUserManServer->RecordSQL(csInfo,0); while (true) { int nSlave = (int)soap_accept(&Soap_service); if (nSlave < 0) { //csInfo.Format(_T("soap_accept调用失败...")); csInfo.LoadString(IDS_CALL_FAILD); pUserManServer->RecordOperaInfo(csInfo,6); pUserManServer->RecordSQL(csInfo,6); soap_print_fault(&Soap_service, stderr); return -1; } soap_serve(&Soap_service); soap_destroy(&Soap_service); soap_end(&Soap_service); } return 0; }
最新发布
05-30
以下是对代码进行的优化: 1. 声明变量时最好直接初始化,避免未初始化的变量被使用。 2. 将字符串转换为const char*时,可以直接使用CString的GetString()函数。 3. 在循环中调用soap_destroy和soap_end函数是不必要的,只需要在循环之前调用一次即可。 4. 将错误码与字符串绑定在一起,可以减少代码量。 5. 将常量字符串存储在全局变量中,避免重复加载。 6. 可以尝试将一些重复的代码封装为函数,提高代码的复用性。 优化后的代码如下所示: ``` DWORD WINAPI SoapServerThread(LPVOID pParam) { CUserManagerServerDlg* pUserManServer = static_cast<CUserManagerServerDlg*>(pParam); struct soap Soap_service; soap_init(&Soap_service); Soap_service.pUserManagerServerDlg = pUserManServer; Soap_service.bind_flags = SO_REUSEADDR; const char* chIP = CStringA(pUserManServer->m_csIP.GetString()); static const int BIND_FAILD = 6; static const int CALL_FAILD = 6; static const int INFO = 0; static const CStringA STR_BIND_FAILD("用户服务器绑定失败"); static const CStringA STR_CANNOT_EMPTY("IP和端口号不允许为空!"); static const CStringA STR_STARTED("用户服务器已启动..."); static const CStringA STR_LISTEN("监听"); if (pUserManServer->m_csIP.IsEmpty() || !pUserManServer->m_nPort) { pUserManServer->RecordOperaInfo(STR_CANNOT_EMPTY, BIND_FAILD); pUserManServer->RecordSQL(STR_CANNOT_EMPTY, BIND_FAILD); return -1; } CStringA strInfo; strInfo.Format("%s:IP(%s),Port:(%d)", STR_LISTEN.GetString(), chIP, pUserManServer->m_nPort); pUserManServer->RecordOperaInfo(CString(strInfo), INFO); pUserManServer->RecordSQL(CString(strInfo), INFO); int nBindErr; if ((nBindErr = soap_bind(&Soap_service, chIP, pUserManServer->m_nPort, 100)) < 0) { strInfo = STR_BIND_FAILD; pUserManServer->RecordOperaInfo(CString(strInfo), BIND_FAILD); pUserManServer->RecordSQL(CString(strInfo), BIND_FAILD); soap_print_fault(&Soap_service, stderr); return -1; } strInfo = STR_STARTED; pUserManServer->RecordOperaInfo(CString(strInfo), INFO); pUserManServer->RecordSQL(CString(strInfo), INFO); while (true) { int nSlave = (int)soap_accept(&Soap_service); if (nSlave < 0) { strInfo = STR_CALL_FAILD; pUserManServer->RecordOperaInfo(CString(strInfo), CALL_FAILD); pUserManServer->RecordSQL(CString(strInfo), CALL_FAILD); soap_print_fault(&Soap_service, stderr); return -1; } soap_serve(&Soap_service); } soap_destroy(&Soap_service); soap_end(&Soap_service); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值