UDP is a simple, unreliable datagram protocol: UDP sockets are connectionless. UDP address formats are identical to those used by TCP. In particular UDP provides a port identifier in addition to the normal Internet address format. The UDP port space is separate from the TCP port space (i.e. a UDP port may not be "connected" to a TCP port). In addition broadcast packets may be sent (assuming the underlying network supports this) by using a reserved "broadcast address"; this address is network interface dependent. Client-side messages with SOAP-over-UDP endpoint URLs (soap.udp://...) will be automatically transmitted as datagrams. Server-side applications should set the SOAP_IO_UDP mode flag to accept UDP requests, e.g. using soap_init1 or soap_set_mode. The maximum message length for datagram packets is restricted by the buffer size SOAP_BUFLEN, which is 65536 by default, unless compiled with WITH_LEAN to support small-scale embedded systems. For UDP transport SOAP_BUFLEN must not exceed the maximum UDP packet size 65536 (the size of datagram messages is constrained by the UDP packet size 216=65536 as per UDP standard). You can use gzip compression to reduce the message size, but note that compressed SOAP-over-UDP is a gSOAP-specific feature because it is not part of the SOAP-over-UDP specification. The SOAP-over-UDP specification relies on WS-Addressing. The wsa.h file in the import directory defines the WS-Addressing elements for client and server applications. The gSOAP implementation conforms to the SOAP-over-UDP requirements:
The following additional features are also available, but are not supported by the SOAP-over-UDP specification:
A SOAP-over-UDP application MUST use WS-Addressing to control message delivery as per SOAP-over-UDP specification. The wsa.h file in the import directory defines the WS-Addressing elements. To include the WS-Addressing elements in the SOAP Header for messaging, a struct SOAP_ENV__Header structure must be defined in your header file with the appropriate WS-Addressing elements. For example:
#import "wsa.h" struct SOAP_ENV__Header { mustUnderstand _wsa__MessageID wsa__MessageID 0; mustUnderstand _wsa__RelatesTo *wsa__RelatesTo 0; mustUnderstand _wsa__From *wsa__From 0; mustUnderstand _wsa__ReplyTo *wsa__ReplyTo 0; mustUnderstand _wsa__FaultTo *wsa__FaultTo 0; mustUnderstand _wsa__To wsa__To 0; mustUnderstand _wsa__Action wsa__Action 0; }; |
We also included a //gsoap wsa schema import directive in the wsa.h file to enable the generation of WSDL specifications that import (instead of includes) the WS-Addressing elements. Note that the //gsoapopt w directive must not be present in your header file to enable WSDL generation. One-way SOAP-over-UDP messages (see Section 7.3) should be declared to include the wsa:MessageID, wsa:To, and wsa:Action elements in the SOAP Header of the request message as follows:
//gsoap ns service method-header-part: sendString wsa__MessageID //gsoap ns service method-header-part: sendString wsa__To //gsoap ns service method-header-part: sendString wsa__Action int ns__sendString(char *str, void); |
Request-response SOAP-over-UDP messages should be declared to include the wsa:MessageID, wsa:To, wsa:Action, and wsa:ReplyTo elements in the SOAP Header of the request message, and the the wsa:MessageID, wsa:To, wsa:Action, and wsa:RelatesTo elements in the SOAP Header of the response message:
//gsoap ns service method-header-part: echoString wsa__MessageID //gsoap ns service method-header-part: echoString wsa__To //gsoap ns service method-header-part: echoString wsa__Action //gsoap ns service method-input-header-part: sendString wsa__ReplyTo //gsoap ns service method-output-header-part: echoString wsa__RelatesTo int ns__echoString(char *str, char **res); |
For the content requirements of these elements, please consult the SOAP-over-UDP specification and/or read the next sections explaining SOAP-over-UDP unicast, multicast, one-way, and request-response client and server applications.
This example assumes that the gSOAP header file includes the SOAP Header with WS-Addressing elements and the ns__sendString function discussed in Section 18.1
struct soap soap; struct SOAP_ENV__Header header; // the SOAP Header soap_init(&soap); soap.send_timeout = 1; // 1s timeout soap_default_SOAP_ENV__Header(&soap, &header); // init SOAP Header header.wsa__MessageID = "message ID"; header.wsa__To = "server URL"; header.wsa__Action = "server action"; soap.header = &header; // bind the SOAP Header for transport // Send the message over UDP: if (soap_send_ns__echoString(&soap, "soap.udp://...", NULL, "hello world!")) soap_print_fault(&soap, stderr); // report error soap_end(&soap); // cleanup soap_destroy(&soap); // cleanup soap_done(&soap); // close connection (should not use soap struct after this) |
This example is similar to the one-way unicast example discussed above, but uses a broadcast address and the SO_BROADCAST socket option:
struct soap soap; struct SOAP_ENV__Header header; // the SOAP Header in_addr_t addr = inet_addr("1.2.3.4"); // optional soap_init(&soap); soap.send_timeout = 1; // 1s timeout soap.connect_flags = SO_BROADCAST; // required for broadcast soap.ipv4_multicast_if = &addr; // optional for IPv4: see setsockopt IPPROTO_IP IP_MULTICAST_IF soap.ipv6_multicast_if = addr; // optional for IPv6: multicast sin6_scope_id soap.ipv4_multicast_ttl = 1; // optional, see setsockopt IPPROTO_IP, IP_MULTICAST_TTL soap_default_SOAP_ENV__Header(&soap, &header); // init SOAP Header header.wsa__MessageID = "message ID"; header.wsa__To = "server URL"; header.wsa__Action = "server action"; soap.header = &header; // bind the SOAP Header for transport // Send the message over UDP to a broadcast address: if (soap_send_ns__echoString(&soap, "soap.udp://...", NULL, "hello world!")) soap_print_fault(&soap, stderr); // report error soap_destroy(&soap); // cleanup soap_end(&soap); // cleanup soap_done(&soap); // close connection (should not use soap struct after this) |
Please refer to the socket options for IPPROTO_IP IP_MULTICAST_IF to specify the default interface for multicast datagrams to be sent from. This is a struct in_addr (in_addr_t for sin6_scope_id) interface value. Otherwise, the default interface set by the system administrator will be used (if any). Please refer to the socket options for IPPROTO_IP IP_MULTICAST_TTL to limit the lifetime of the packet. Multicast datagrams are sent with a default value of 1, to prevent them to be forwarded beyond the local network. This parameter can be set between 1 to 255.
This example assumes that the gSOAP header file includes the SOAP Header with WS-Addressing elements and the ns__echoString function discussed in Section 18.1
struct soap soap; struct SOAP_ENV__Header header; // the SOAP Header struct wsa__EndpointReferenceType replyTo; // (anonymous) reply address char *res; // server response soap_init(&soap); soap.send_timeout = 1; // 1s timeout soap.recv_timeout = 1; // 1s timeout soap_default_SOAP_ENV__Header(&soap, &header); // init SOAP Header soap_default_wsa__EndpointReferenceType(&soap, &replyTo); // init reply address replyTo.Address = "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous"; header.wsa__MessageID = "message ID"; header.wsa__To = "server URL"; header.wsa__Action = "server action"; header.wsa__ReplyTo = &replyTo; soap.header = &header; // bind the SOAP Header for transport // Send and receive messages over UDP: if (soap_call_ns__echoString(&soap, "soap.udp://...", NULL, "hello world!", &res)) { if (soap.error == SOAP_EOF && soap.errnum == 0) // Timeout: no response from server (message already delivered?) else soap_print_fault(&soap, stderr); } else // UDP server response is stored in 'res' // check SOAP header received, if applicable check_header(&soap.header); soap_destroy(&soap); // cleanup soap_end(&soap); // cleanup soap_done(&soap); // close connection (should not use soap struct after this) |
This example is similar to the request-response unicast example discussed above, but uses a broadcast address and the SO_BROADCAST socket option. Because we expect to receive multiple responses, we also need to use separate request-response messages to send one request and consume multiple responses. In this example we defined a bcastString request and a bcastStringResponse response message, which are essentially declared as one-way messages in the header file:
//gsoap ns service method-header-part: bcastString wsa__MessageID //gsoap ns service method-header-part: bcastString wsa__To //gsoap ns service method-header-part: bcastString wsa__Action //gsoap ns service method-header-part: bcastString wsa__ReplyTo int ns__bcastString(char *str, void); //gsoap ns service method-header-part: bcastStringResponse wsa__MessageID //gsoap ns service method-header-part: bcastStringResponse wsa__To //gsoap ns service method-header-part: bcastStringResponse wsa__Action //gsoap ns service method-header-part: bcastStringResponse wsa__RelatesTo int ns__bcastStringResponse(char *res, void); |
To obtain response one-way operations, use the wsdl2h -b option. The client code includes a loop to receive response messages until a timeout occurs:
struct soap soap; struct SOAP_ENV__Header header; struct wsa__EndpointReferenceType replyTo; char *res; soap_init(&soap); soap.connect_flags = SO_BROADCAST; soap.send_timeout = 1; // 1s timeout soap.recv_timeout = 1; // 1s timeout soap_default_SOAP_ENV__Header(&soap, &header); soap.header = &header; soap_default_wsa__EndpointReferenceType(&soap, &replyTo); replyTo.Address = "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous"; header.wsa__MessageID = "message ID"; header.wsa__To = "server URL"; header.wsa__Action = "server action"; header.wsa__ReplyTo = &replyTo; if (soap_send_ns__bcastString(&soap, "soap.udp://...", NULL, "hello world!")) soap_print_fault(&soap, stderr); else { for (;;) { if (soap_recv_ns__bcastStringResponse(&soap, &res)) break; // Got response 'res' from a server } if (soap.error == SOAP_EOF && soap.errnum == 0) // Timeout: no more messages received else soap_print_fault(&soap, stderr); } soap_destroy(&soap); // cleanup soap_end(&soap); // cleanup soap_done(&soap); // close connection (should not use soap struct after this) |
Note that a server for the bcastString does not need to use two-one way messages. Thus, multicast request-response message pattern can be declared and implemented as request-response operations at the server side.
The following example code illustrates a SOAP-over-UDP server for one-way sendString and request-response echoString messages. This example assumes that the gSOAP header file includes the SOAP Header with WS-Addressing elements and the ns__echoString function discussed in Section 18.1.
int main() { struct soap soap; soap_init1(&soap, SOAP_IO_UDP); // must set UDP flag // bind to host (NULL=current host) and port: if (!soap_valid_socket(soap_bind(&soap, host, port, 100))) { soap_print_fault(&soap, stderr); exit(1); } for (;;) { if (soap_serve(&soap)) soap_print_fault(&soap, stderr); // report the problem soap_destroy(&soap); soap_end(&soap); } soap_done(&soap); // close connection } int ns__echoString(struct soap *soap, char *str, char **res) { if (!soap->header) return soap_sender_fault(soap, "No SOAP header", NULL); if (!soap->header->wsa__MessageID) return soap_sender_fault(soap, "No WS-Addressing MessageID", NULL); soap->header->wsa__RelatesTo = (struct wsa__Relationship*)soap_malloc(soap, sizeof(struct wsa__Relationship)); soap_default_wsa__Relationship(soap, soap->header->wsa__RelatesTo); soap->header->wsa__RelatesTo->__item = soap->header->wsa__MessageID; // must check for duplicate messages if (check_received(soap->header->wsa__MessageID)) { // Request message already received return SOAP_STOP; // don't return response } if (!soap->header->wsa__ReplyTo || !soap->header->wsa__ReplyTo->Address) return soap_sender_fault(soap, "No WS-Addressing ReplyTo address", NULL); soap->header->wsa__To = soap->header->wsa__ReplyTo->Address; soap->header->wsa__MessageID = soap_strdup(soap, soap_int2s(soap, id_count++)) ; soap->header->wsa__Action = "http://genivia.com/udp/echoStringResponse"; *res = str; return SOAP_OK; } int ns__sendString(struct soap *soap, char *str) { if (!soap->header) return SOAP_STOP; if (!soap->header->wsa__MessageID) return SOAP_STOP; // must check for duplicate messages if (check_received(soap->header->wsa__MessageID)) return SOAP_STOP; return SOAP_OK; } int ns__sendStringResponse(struct soap *soap, char *res) { return SOAP_NO_METHOD; } // we don't expect to serve this message |
The server binds to a host and port and accepts messages in a tight sequential loop. Because UDP does not have the equivalent of an accept the messages cannot be dispatched to threads, the soap_serve waits for a message and immediately accepts it. You can use a receive timeout to make soap_serve non-blocking. To obtain response one-way operations from a WSDL, use the wsdl2h -b option. This produces additional one-way operations to support asynchronous handling of response messages in the same way requests are handled.
For UDP multicast support, follow the suggestions in Section 18.6 and change the initialization parts of the code to enable UDP multicast port binding by to telliing the kernel which multicast groups you are interested in:
int main() { struct soap soap; struct ip_mreq mcast; soap_init1(&soap, SOAP_IO_UDP); if (!soap_valid_socket(soap_bind(&soap, host, port, 100))) { soap_print_fault(&soap, stderr); exit(1); } mcast.imr_multiaddr.s_addr = inet_addr("put IP multicast address of group here"); mcast.imr_interface.s_addr = htonl(INADDR_ANY); if (setsockopt(soap.master, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mcast, sizeof(mcast))<0) ... error ... |