📄 udpstack.cxx
字号:
/* ==================================================================== * The Vovida Software License, Version 1.0 * * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The names "VOCAL", "Vovida Open Communication Application Library", * and "Vovida Open Communication Application Library (VOCAL)" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact vocal@vovida.org. * * 4. Products derived from this software may not be called "VOCAL", nor * may "VOCAL" appear in their name, without prior written * permission of Vovida Networks, Inc. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * * ==================================================================== * * This software consists of voluntary contributions made by Vovida * Networks, Inc. and many individuals on behalf of Vovida Networks, * Inc. For more information on Vovida Networks, Inc., please see * <http://www.vovida.org/>. * */static const char* const UdpStack_cxx_Version = "$Id: UdpStack.cxx,v 1.35.2.1 2003/01/30 22:39:10 bko Exp $";/* TODO List * - add sendTo function to allow you to specifiy different destinations * - add recvFrom function that tell you who the packet came from * - add non blockinge version fo send and receive * - derive "ReliableUDP" stack that takes care of retransmissions * - look into using MSG_ERRQUEUE to check for ICMP errors * - check portability * - support IP6 * - stress test */#include "global.h"#include <fstream>#include <assert.h>#include <string.h> // for memset#include <errno.h>#include <iostream>#include <stdio.h>#include <netdb.h>#include <sys/types.h>#include <netinet/in.h> // for struct sockaddr_in#include <stdlib.h>#include <strstream>#include <arpa/inet.h>#include <sys/socket.h>#include "VTime.hxx"#include <unistd.h>#include <sys/uio.h>#ifndef __vxworks#include <fcntl.h>#endif#ifdef __APPLE__#include "TransportCommon.hxx"#endif#include "vovida-endian.h"#if defined(__svr4__)#include <xil/xil.h>#if (XIL_API_MINOR_VERSION - 0 == 3)typedef int socklen_t;#endif#endif#ifdef __FreeBSD__#include <isc/eventlib.h>#endif#ifdef __APPLE__typedef int socklen_t;#endif#include "UdpStack.hxx"#include "cpLog.h"#include "vsock.hxx"#include "LockHelper.hxx"#include "InitTransport.hxx"#include "NetworkConfig.hxx"// This is a file that contains multicast definitions for window support// contact nismail@cisco.com#ifdef WIN32#include "W32McastCfg.hxx"#endifstatic VMutex G_lock;static const char separator[7] = "\n****\n";UdpStack::UdpStack ( const NetworkAddress* desHost /* null if this is the server */ , int minPort, int maxPort, UdpMode udpMode, bool log_flag, bool isMulticast) : packetLossProbability( float(0.0) ), numBytesReceived (0), numPacketsReceived (0), numBytesTransmitted (0), numPacketsTransmitted (0), mode (sendrecv), logFlag (log_flag), blockingFlg(true){ initTransport(); data = new UdpStackPrivateData(); assert(data); mode = udpMode; data->socketFd = socket(NetworkConfig::instance().getAddrFamily(), SOCK_DGRAM, IPPROTO_UDP); cpLog (LOG_DEBUG_STACK, "UdpStack socketFd = %d", data->socketFd); if ( data->socketFd < 0 ) {#if !defined(WIN32) int err = errno; strstream errMsg; errMsg << "UdpStack::::UdpStack error during socket creation:"; errMsg << "Reason " << strerror(err) << ends; cpLog(LOG_ERR, errMsg.str()); throw UdpStackException(errMsg.str());#else int err = WSAGetLastError(); cpLog(LOG_ERR, "UdpStack: socket failed: %d", err); assert(0);#endif } int buf1 = 1; int len1 = sizeof(buf1); int rcvbuf = 0; int rcvbufnew = 240 * 1024; int rcvbufnewlen = sizeof(rcvbufnew); int sndbuf = 0; unsigned int rcvbuflen = 1; unsigned int sndbuflen = 1;#ifdef __linux__ struct protoent * protoent; protoent = getprotobyname("icmp"); if (!protoent) { fprintf(stderr, "Cannot get icmp protocol\n"); } else { if (setsockopt(data->socketFd, protoent->p_proto, SO_BSDCOMPAT, (char*)&buf1, len1) == -1) { fprintf(stderr, "setsockopt error SO_BSDCOMPAT :%s\n", strerror(errno)); }#if 1 if (setsockopt(data->socketFd, SOL_SOCKET, SO_RCVBUF, (int *)&rcvbufnew, rcvbufnewlen) == -1) { fprintf(stderr, "setsockopt error SO_RCVBUF :%s\n", strerror(errno)); }#endif if (getsockopt(data->socketFd, SOL_SOCKET, SO_RCVBUF, (int*)&rcvbuf, &rcvbuflen) == -1) { fprintf(stderr, "getsockopt error SO_RCVBUF :%s\n", strerror(errno)); } else { cpLog(LOG_DEBUG, "SO_RCVBUF = %d, rcvbuflen =%d" , rcvbuf , rcvbuflen); } if (getsockopt(data->socketFd, SOL_SOCKET, SO_SNDBUF, (int*)&sndbuf, &sndbuflen) == -1) { fprintf(stderr, "getsockopt error SO_SNDBUF :%s\n", strerror(errno)); } else { cpLog(LOG_DEBUG, "SO_SNDBUF = %d, sndbuflen = %d" , sndbuf , sndbuflen); } }#endif if (isMulticast) { // set option to get rid of the "Address already in use" error if (setsockopt(data->socketFd, SOL_SOCKET, SO_REUSEADDR, (char*)&buf1, len1) == -1) { fprintf(stderr, "setsockopt error SO_REUSEADDR :%s", strerror(errno)); }#if defined(__FreeBSD__) || defined(__APPLE__) if (setsockopt(data->socketFd, SOL_SOCKET, SO_REUSEPORT, (char*)&buf1, len1) == -1) { fprintf(stderr, "setsockopt error SO_REUSEPORT :%s", strerror(errno)); }#endif } switch (mode) { case inactive : { cpLog(LOG_INFO, "Udp stack is inactive"); cpLog(LOG_ERR, "desHost is saved for future use."); doClient(desHost); } break; case sendonly : { if ( desHost ) { // set the remote address doClient(desHost); } else { cpLog(LOG_DEBUG_STACK, "sendonly Udp stack, desHost is needed by using setDestination()"); } } break; case recvonly : { if ( desHost ) { cpLog(LOG_ERR, "recvonly Udp stack, desHost is saved for future use."); doClient(desHost); } else { // only receive, do bind socket to local port doServer(minPort, maxPort); } } break; case sendrecv : { if ( desHost ) { // receive and send, // bind the socket to local port and set the remote address doServer(minPort, maxPort); doClient(desHost); } else { // only receive, do bind socket to local port doServer(minPort, maxPort); cpLog(LOG_DEBUG_STACK, "sendrecv Udp stack, desHost is needed by using setDestination()"); } } break; default : cpLog(LOG_ERR, "undefined mode for udp stack"); break; } // logFlag = true; strstream logFileNameRcv; strstream logFileNameSnd; if (logFlag) { in_log = new ofstream(logFileNameRcv.str(), ios::app); if (!in_log) { cerr << "Cannot open file " << logFileNameRcv.str() << endl; logFileNameRcv.freeze(false); logFlag = false; } else { in_log->write ("UdpRcv\n", 7); strstream lcPort; lcPort << "localPort: " << getTxPort() << "\n" << char(0); in_log->write(lcPort.str(), strlen(lcPort.str())); lcPort.freeze(false); logFileNameRcv.freeze(false); rcvCount = 0; } out_log = new ofstream(logFileNameSnd.str(), ios::app); if (!out_log) { cerr << "Cannot open file " << logFileNameSnd.str() << endl; logFileNameSnd.freeze(false); logFlag = false; } else { if (! logFlag) logFlag = true; out_log->write ("UdpSnd\n", 7); logFileNameSnd.freeze(false); sndCount = 0; } }}voidUdpStack::doServer ( int minPort, int maxPort ) // these are lcoal ports{ /* cpLog (LOG_DEBUG_STACK, "UdpStack::doServer"); cpLog (LOG_DEBUG_STACK, "minPort = %d, maxPort = %d", minPort, maxPort); */ LockHelper helper(_lock); // this is a server if ( (minPort == -1) && (maxPort == -1) ) { minPort = 1024; maxPort = 65534; } if ( maxPort == -1 ) { maxPort = minPort; } // reset name now that the port range is defined strstream aName; aName << "-receiver-" << ":" << "[" << minPort << "-" << maxPort << "]" << char(0); setLclName( aName.str() ); aName.freeze(false); // find a port to use int portOk = false; int err = 0; int bError = false; struct addrinfo hints; struct addrinfo *sa; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; hints.ai_family = NetworkConfig::instance().getAddrFamily(); hints.ai_socktype = SOCK_DGRAM; // here it assigns the port, the local port // & bind the addr(INADDR_ANY+port) to the socket for (int localPort = minPort; localPort <= maxPort; localPort++ ) { char currport[6]; sprintf(currport, "%u", localPort); cpLog(LOG_DEBUG_STACK, "getaddrinfo()"); int error = getaddrinfo(NULL, currport, &hints, &sa); if (error) { perror(gai_strerror(error)); continue; } cpLog(LOG_DEBUG, "Udp bind() fd =%d, port=%s", data->socketFd, currport); //port = %d(h_order), %d(n_order)", data->socketFd, localPort, (data->localAddr).sin6_port); if (bind(data->socketFd, sa->ai_addr, sa->ai_addrlen)) { err = errno; #if !defined(WIN32) if ( err == EADDRINUSE ) { #else /* Fix suggested by Anandprasanna Gaitonde, prasanna@controlnet.co.in */ // err = WSAGetLastError(); // if ( err == WSAEADDRINUSE ) // #endif freeaddrinfo(sa); continue; // this port is in use - try the next one } int err = errno; strstream errMsg; errMsg << "UdpStack<" << getLclName() << ">::UdpStack error during socket bind: "; errMsg << strerror(err); errMsg << char(0); bError = true; cpLog(LOG_ERR, "%s", errMsg.str()); } else { portOk = true; memcpy(data->localAddr, sa->ai_addr, sa->ai_addrlen); bError = false; if(sa->ai_family == AF_INET6) { cpLog(LOG_DEBUG, "(IPv6) Udp bound to fd = %d, port = %d",data->socketFd, localPort); //Set the sockoption so that we get get source IP //when running on the same host int on=1; setsockopt(data->socketFd, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)); } else { cpLog(LOG_DEBUG, "(IPv4) Udp bound to fd = %d, port = %d",data->socketFd, localPort); } } freeaddrinfo(sa); if (portOk) break; } if (bError) throw UdpStackException("fecked");//errMsg.str()); // deal with errors if (!portOk) { // all ports are in use //localPort = -1; strstream errMsg; errMsg << "UdpStack<" << getLclName() << ">::UdpStack all ports in range " << minPort << " to " << maxPort << " are in use."; errMsg << char(0); cpLog(LOG_ERR, errMsg.str()); throw UdpStackExceptionPortsInUse(errMsg.str()); errMsg.freeze(false); assert(0); } // reset name now that the port is defined strstream aName2; aName2 << "-receiver-" << ":" << minPort << char(0); setLclName( aName2.str() ); aName2.freeze(false);}voidUdpStack::doClient ( const NetworkAddress* desHost) // This is the destination port{ //cpLog (LOG_DEBUG_STACK, "UdpStack::doClient"); //cpLog (LOG_DEBUG_STACK, "desHost = %s, desPort = %d", // desHost->getIpName().c_str(), desHost->getPort()); // this is a client assert (desHost); desHost->getSockAddr(data->remoteAddr);}voidUdpStack::connectPorts(){ if ((mode == recvonly) || (mode == inactive)) { cpLog(LOG_ERR, "The UdpStack is recvonly or inactive."); return ; } int result; // conenct to server if ((result = connect(data->socketFd, (struct sockaddr*) & (data->localAddr), sizeof(*data->localAddr))) != 0) { int err = errno; strstream errMsg; errMsg << "UdpStack<" << getLclName() << " " << getRmtName() << ">::UdpStack error during socket connect: "; errMsg << strerror(err); errMsg << char(0); cpLog(LOG_ERR, errMsg.str()); throw UdpStackException(errMsg.str()); errMsg.freeze(false); assert(0); }}// After testing, this method currently is not working on Linux and Sun4// possibily because the system are not supporting it.// Now the work around is to call connect twice.voidUdpStack::disconnectPorts(){ if ((mode == recvonly) || (mode == inactive)) { cpLog(LOG_ERR, "The UdpStack is recvonly or inactive."); return ; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -