bind failed in soap_bind() 问题解决

文章详细介绍了如何在gsoap协议中解决服务异常退出后,再次打开服务时出现的端口号绑定错误,通过在bind函数前使用setsockopt函数设置socket状态,避免了端口被绑定的问题,提供了具体的实现方法。
使用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,不会出现端口号绑定的情况了
/****************************************************************************** * Copyright (c) 2018-2018 TP-Link Systems Inc. * * Filename: onvif_srv.c * Version: 1.0 * Description: ONVIF 服务模块 * Author: liyijie<liyijie@tp-link.com.cn> * Date: 2018-10-30 ******************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include "onvif_srv.h" #include "soap_global.h" #include "soap_parse.h" #include "soap_pack.h" #include "soap_tds.h" #include "soap_trt.h" #include "soap_tr2.h" #include "soap_tev.h" #include "soap_timg.h" #include "soap_tan.h" #include "soap_tptz.h" #include "http_parser.h" SOAP_CONTEXT *g_soap_for_main = NULL; S32 g_index_for_main = -1; /*主SOAP上下文*/ S32 g_sock_for_main = -1; /*套接字索引*/ /****************************************************************************** * 函数名称: onvif_proc_data_srv() * 函数描述: onvif 报文处理函数 * 输 入: context -- http报文内容 * 输 出: N/A * 返 回 值: ERROR/OK * 流 程: 检查context和HTTP内容是否有效。 根据端口号(context->port)确定使用哪个SOAP上下文(主服务端口或订阅端口)。 初始化SOAP上下文,绑定套接字等信息。 ******************************************************************************/ S32 onvif_proc_data_srv(CONTEXT *context) { S32 length = 0; char *xml_buf = NULL; char **xml_str = NULL; SOAP_CONTEXT *soap = NULL; SOAP_NODE *p = NULL; SUBSCRIPTION *item = NULL; if (context == NULL) { ONVIF_ERROR("context == NULL."); return ERROR; } if (context->content_start == NULL || context->content_end == NULL || (length = context->content_end - context->content_start) <= 0) { ONVIF_ERROR("http content error."); return ERROR; } /* 如果http状态码不是200则直接返回 */ if (HTTP_REQ_OK != context->code) { ONVIF_ERROR("context->code is %d.", context->code); if (HTTP_CLIENT_DEAD == context->code) { context->code = HTTP_INTERNAL_ERROR; } return ERROR; } if (context->port == ONVIF_PORT_FOR_MAIN || context->port == ONVIF_PORT_FOR_HTTP) { soap = g_soap_for_main; } else { p = g_sub_list; while (p) { if (p->data) { item = (SUBSCRIPTION *)p->data; if (item->sub_port == (S32)context->port) { break; } } p = p->next; } if (p == NULL || item == NULL) { ONVIF_ERROR("port not support subscription."); context->code = HTTP_BAD_REQUEST; return ERROR; } soap = item->p_soap; } context->onvif_soap = (void *)soap; soap_init(soap); soap->sock = context->sock; soap->port = context->port; soap->use_udp = FALSE; soap->http_context = context; soap->in_ip = context->client.sin_addr.s_addr; xml_buf = context->content_start; xml_str = (char **)&xml_buf; if (OK != soap_xml_parse(soap, xml_str, length)) { ONVIF_ERROR("soap_xml_parse error."); context->code = HTTP_BAD_REQUEST; return ERROR; } if (soap->tag[0] == '\0') { ONVIF_ERROR("no request tag."); context->code = HTTP_BAD_REQUEST; return ERROR; } if (OK != soap_serve_request(soap)) { ONVIF_ERROR("soap_serve_request failed."); context->code = HTTP_INTERNAL_ERROR; return ERROR; } return OK; } /****************************************************************************** 功能:检查给定的端口是否用于ONVIF服务(主端口或订阅端口)。 输入:U16 port,待检查的端口号。 输出:返回TRUE(支持)或FALSE(不支持)。 逻辑: 如果端口是主服务端口(ONVIF_PORT_FOR_MAIN),返回TRUE。 否则遍历订阅列表g_sub_list,检查是否有订阅项使用该端口。 ******************************************************************************/ LOCAL BOOL onvif_check_port(U16 port) { SOAP_NODE *p = NULL; SUBSCRIPTION *item = NULL; if (port == ONVIF_PORT_FOR_MAIN) { return TRUE; } p = g_sub_list; while (p) { if (p->data) { item = (SUBSCRIPTION *)p->data; if (item->sub_port == (S32)port) { return TRUE; } } p = p->next; } return FALSE; } /****************************************************************************** 功能:根据处理结果生成HTTP响应。如果处理成功,发送SOAP响应;否则发送HTTP错误响应。 输入:CONTEXT *context,包含处理后的上下文(包括SOAP上下文)。 输出:返回OK或ERROR。 流程: 从context中获取SOAP上下文。 如果HTTP状态码是200(OK)且存在响应XML内容,调用onvif_send_http_rsp_packet发送SOAP响应。 否则,调用http_make_response发送HTTP错误响应。 最后重置context的内容缓冲区。 ******************************************************************************/ LOCAL S32 onvif_make_response(CONTEXT *context) { SOAP_CONTEXT *soap = NULL; int ret = OK; if (context == NULL) { ONVIF_ERROR("context == NULL."); ret = ERROR; goto out; } soap = (SOAP_CONTEXT *)context->onvif_soap; if (soap == NULL) { ONVIF_ERROR("soap == NULL."); ret = ERROR; goto out; } if (context->code == HTTP_REQ_OK) { if (soap->use_udp == FALSE && soap->xml_buf.start != NULL && soap->xml_buf.start != soap->xml_buf.last) { ONVIF_TRACE("Onvif_make_response return onvif_send_http_rsp_packet."); ret = onvif_send_http_rsp_packet(soap); goto out; } else { ONVIF_TRACE("Onvif_make_response return OK."); ret = OK; goto out; } } ONVIF_TRACE("Onvif_make_response return http_make_response."); ret = http_make_response(context); out: /* 清除content内容 */ http_reset_context(context); return ret; } /****************************************************************************** 功能:启动ONVIF服务。初始化主SOAP上下文,绑定监听端口,设置套接字选项,并注册HTTP处理函数。 输入:无。 输出:返回OK或ERROR。 步骤: 创建主SOAP上下文(g_soap_for_main)。 使用http_group_listen_port在ONVIF_PORT_FOR_MAIN端口上监听。 设置套接字的接收和发送缓冲区大小。 调用http_group_srv_add注册ONVIF服务的回调函数:onvif_check_port(端口检查)、onvif_proc_data_srv(数据处理)、 onvif_make_response(响应生成)。 ******************************************************************************/ S32 onvif_srv_start() { ONVIF_TRACE("Onvif services start"); g_soap_for_main = new_soap(); if (g_soap_for_main == NULL) { ONVIF_ERROR("malloc g_soap_for_main failed"); return ERROR; } g_index_for_main = http_group_listen_port(GROUP_ONVIF, ONVIF_PORT_FOR_MAIN, &g_sock_for_main); if (0 > g_index_for_main) { ONVIF_ERROR("onvif server start failed!"); free_soap(g_soap_for_main); g_soap_for_main = NULL; return ERROR; } if (0 > onvif_set_sock_rcvbuf(g_sock_for_main, ONVIF_SOCKET_RECV_BUF_SIZE)) { ONVIF_WARN("setsockopt SO_RCVBUF failed."); } if (0 > onvif_set_sock_sndbuf(g_sock_for_main, ONVIF_SOCKET_SEND_BUF_SIZE)) { ONVIF_WARN("setsockopt SO_SNDBUF failed."); } http_group_srv_add(GROUP_ONVIF, onvif_check_port, NULL, onvif_proc_data_srv, onvif_make_response); return OK; } /****************************************************************************** * 函数名称: onvif_srv_stop() * 函数描述: onvif server处理模块停止函数,释放数据 * 输 入: N/A * 输 出: N/A * 返 回 值: ERROR/OK * 操 作: 移除套接字(inet_del_socket)、关闭套接字(close)、释放主SOAP上下文(free_soap)。 ******************************************************************************/ void onvif_srv_stop() { if (g_index_for_main > 0) { inet_del_socket(g_index_for_main); g_index_for_main = -1; } if (g_sock_for_main > 0) { close(g_sock_for_main); g_sock_for_main = -1; } free_soap(g_soap_for_main); g_soap_for_main = NULL; return; } /****************************************************************************** * 函数名称: onvif_srv_remove_socket() * 函数描述: onvif server处理模块固件升级时只移除socket,资源设备重启时回收 * 输 入: N/A * 输 出: N/A * 返 回 值: ERROR/OK ******************************************************************************/ void onvif_srv_remove_socket() { if (g_index_for_main > 0) { inet_del_socket(g_index_for_main); g_index_for_main = -1; } if (g_sock_for_main > 0) { close(g_sock_for_main); g_sock_for_main = -1; } return; } /****************************************************************************** * 函数名称: onvif_srv_handle_init() * 函数描述: 注册onvif server模块需要的soap处理函数 * 输 入: N/A * 输 出: N/A * 返 回 值: N/A ******************************************************************************/ void onvif_srv_handle_init() { soap_tds_handle_init(); soap_trt_handle_init(); soap_tr2_handle_init(); soap_tev_handle_init(); soap_timg_handle_init(); soap_tan_handle_init(); soap_tptz_handle_init(); } /****************************************************************************** 模块功能总结 服务启动与停止:通过onvif_srv_start和onvif_srv_stop管理ONVIF服务的生命周期。 请求处理:使用onvif_proc_data_srv解析HTTP请求,将其转换为SOAP消息并分发给对应的服务处理函数。 端口管理:支持主服务端口和多个订阅端口,通过onvif_check_port验证端口合法性。 响应生成:根据处理结果生成SOAP响应或HTTP错误响应。 服务注册:onvif_srv_handle_init注册所有ONVIF服务(设备发现、媒体服务、事件服务等)的处理函数。 对外接口 初始化:onvif_srv_handle_init(需在服务启动前调用)。 启动服务:onvif_srv_start。 停止服务:onvif_srv_stop。 临时移除套接字:onvif_srv_remove_socket。 数据处理入口:onvif_proc_data_srv(由HTTP框架回调)。 ******************************************************************************/ ——onvif.srv.c哪来的soap_ssl_accept()soap_bind()?你不要总是乱编行不行!!!!!
最新发布
09-02
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值