📄 sockutil.cpp
字号:
// sockutil.cc// code for sockutil.h// copyright SafeTP Development Group, Inc., 2000 Terms of use are as specified in license.txt#include "xassert.h" // xassert#include <string.h> // strlen, memset#include <stdio.h> // printf#include <stdlib.h> // exit, rand, srand#include "socket.h" // socket functions#include "strtokp.h" // StrtokParse#include "nonport.h" // portableSleep, getProcessId#include "sockutil.h" // this module// ---------- diagnostic facilities ------------------// general diagnostic#ifdef SOCKUTIL_DIAGNOSTICS# define DIAGNOSTIC 1#else# define DIAGNOSTIC 0#endif// connection activity: socket open, close#define CONN_DIAGNOSTIC 1// data activity: send and receive#define DATA_DIAGNOSTIC 0// select block/unblock#define SELECT_DIAGNOSTIC 0#if DIAGNOSTIC# include <iostream.h> // cout# define diagnostic(expr) cout << "socket(" << numSocketsOpen() << "): " << expr << endl#else# define diagnostic(expr) ((void)0)#endif#if CONN_DIAGNOSTIC# define connDiagnostic(expr) diagnostic(expr)#else# define connDiagnostic(expr) ((void)0)#endif#if DATA_DIAGNOSTIC# define dataDiagnostic(expr) diagnostic(expr)#else# define dataDiagnostic(expr)#endif#if SELECT_DIAGNOSTIC# define selectDiagnostic(expr) diagnostic(expr)#else# define selectDiagnostic(expr)#endif// ------------------- xSocket ----------------------------xSocket::xSocket(SOCKET s, char const *msg) : xBase(msg), socket(s), graceful(false), temporary(false){}xSocket::xSocket(SOCKET s) : xBase("closed gracefully"), socket(s), graceful(true), temporary(false){}xSocket::xSocket(xSocket const &obj) : xBase(obj), socket(obj.socket), graceful(obj.graceful), temporary(obj.temporary){}xSocket::~xSocket(){}// -------------------- xResolveFailure -----------------------xResolveFailure::xResolveFailure(char const *hn) : xBase(stringb("Failed to resolve hostname \"" << hn << "\"")), hostname(hn){}xResolveFailure::xResolveFailure(xResolveFailure const &obj) : xBase(obj), hostname(obj.hostname){}xResolveFailure::~xResolveFailure(){}// ----------------- open-sockets counting ------------------// for thread safety, reduce visibility by making static and adding// an underscopre to change the namestatic int _socketsOpen = 0;int numSocketsOpen(){ LOCKER return _socketsOpen;}void incOpenSockets(){ LOCKER _socketsOpen++;}void decOpenSockets(){ LOCKER _socketsOpen--;}// ----------------- other standalone routines ------------------------char recvChar(SOCKET s){ char c; int len = basicRecv(s, &c, 1); if (len == 0) { // graceful close xsocketClosed(s); } return c;}// not efficient at all..string recvLine(SOCKET s){ // phasing out usage, so want to see usage //breaker(); //printf(" ** recvLine called **\n"); // (reasonably) efficient growing string stringBuilder buf; // read 2 chars, since we can't begin testing // for a final CRLF until there are at least 2 buf << recvChar(s); buf << recvChar(s); int len = 2; // test.. while (!( buf[len-2]=='\r' && buf[len-1]=='\n' )) { // read another character buf << recvChar(s); len++; } return string(buf, len-2); // return string without the EOL sequence}// NOTE: the network buffers are only guaranteed to hold 1 byte for// peeking. virtually all implementations can hold more, typically 1k// or 4k. but that's still not infinite, so this should only be used// in situations where the expected string is short.string peekLine(SOCKET s){ enum { BUFSIZE=1024 }; // arbitrary maximum (but remember we expect short strings) char buf[BUFSIZE]; char const *crlf; do { // grab what's in the buffer int len = recv(s, buf, BUFSIZE-1, MSG_PEEK); if (len == -1) { xsocket(s, "recv"); } buf[len] = 0; // null terminate so can use strstr // see if a CRLF is in the buffer crlf = strstr(buf, "\r\n"); if (!crlf) { // it's not there // if what we read maxed-out the buffer if (len == BUFSIZE-1) { // then we're not going to find the CRLF.. I don't know // a good action to take here xfailure("peekLine: the buffer filled without seeing a complete line"); } // wait a little bit before checking again portableSleep(1); } } while (!crlf); // ok, a string was found; return it (without CRLF) return string(buf, crlf-buf);}unsigned long recvNBO32(SOCKET s){ unsigned char buf[4]; recvAll(s, (char*)buf, 4); // construct from big-endian return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];}void sendNBO32(SOCKET s, unsigned long value){ // represent as big-endian byte buf[4]; buf[0] = (byte)((value >> 24) & 0xff); buf[1] = (byte)((value >> 16) & 0xff); buf[2] = (byte)((value >> 8) & 0xff); buf[3] = (byte)(value & 0xff); //printf("NBO32 sending: %X %X %X %X\n", // buf[0], buf[1], buf[2], buf[3]); sendAll(s, (char const*)buf, 4);}void recvAll(SOCKET s, char *buf, int len){ int result = recvAllToEOF(s, buf, len); if (result < len) { // this fn throws an exception on EOF xsocketClosed(s); }}int basicRecv(SOCKET s, char *buf, int len){ // block until at least 1 byte read xassert(buf != NULL && len >= 1); int received = recv(s, buf, len, 0); if (received == SOCKET_ERROR) { xsocket(s, "recv"); } return received;}int recvAllToEOF(SOCKET s, char *buf, int len){ int totalRead = 0; while (len != 0) { // block until at least 1 byte read int xmitted = basicRecv(s, buf, len); if (xmitted == 0) { // EOF break; } // quick check to guard against recv writing past end // due to math error in here xassert(xmitted <= len); // advance counters buf += xmitted; len -= xmitted; totalRead += xmitted; } return totalRead;}void sendChar(SOCKET s, char c){ int len = basicSend(s, &c, 1); if (len == 0) { // graceful closure xsocketClosed(s); }}// send all of the specified buffer, blocking// as necessaryvoid sendAll(SOCKET s, char const *buf, int len){ while (len != 0) { // is send guaranteed to block until at least 1 byte sent? yes int sent = basicSend(s, buf, len); if (sent == 0) { xsocketClosed(s); } xassert(sent <= len); // can't send more than was in buffer... buf += sent; len -= sent; }}void sendAllString(SOCKET s, char const *str){ sendAll(s, str, strlen(str));}void sendEOL(SOCKET s){ static char const eol[] = "\r\n"; sendAll(s, eol, 2);}int basicSend(SOCKET s, char const *data, int len){ xassert(data != NULL && len >= 1); int sent = send(s, data, len, 0); if (sent == SOCKET_ERROR) { xsocket(s, "send"); } return sent;}// create a socket, and possibly set SO_RESUSEADDRSOCKET create_socket(){ // create the socket SOCKET s = socket(AF_INET, SOCK_STREAM, 0); if (s == INVALID_SOCKET) { xsocket(s, "socket"); } incOpenSockets(); // during development, it's annoying when the harder crashes cause // us to not be able to reuse a port for a while; it also increases // the # of possible connections for bind-range-of-ports servers // (I'm questioning this decision, since it affects TCP's reliability, // but I can't think of a good way to handle it..) // // it turns out that on win32 SO_REUSEADDR's semantics are different, // namely that it *does* allow two sockets to listen to the same // address (unlike all unix implementations I've encountered); since // this can especially be a problem with the -r switch, we'll skip // this on win32; I also added another escape hatch to disable if // necessary elsewhere #if !defined(__WIN32__) && !defined(DONT_REUSEADDR) { int boolval = 1; int res = setsockopt( s /*socket*/, SOL_SOCKET /*level*/, SO_REUSEADDR /*optname*/, // reuse if possible (char*)&boolval /*optval*/, sizeof(boolval) /*optlen*/); if (res == SOCKET_ERROR) { xsocket(s, "setsockopt", true /*close*/); } } #endif return s;}// return false on failurebool inner_bind_socket(SOCKET s, IPAddress interface, int port){ // create the address structure sockaddr_in saddr; memset((char *)&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(interface); // which interface to bind to saddr.sin_port = htons((short)port); // correct byte order // attach that local address to the socket return bind(s, (sockaddr *)&saddr, sizeof(saddr)) != SOCKET_ERROR;}// throw exception on failurevoid bind_socket(SOCKET s, IPAddress interface, int port){ if (!inner_bind_socket(s, interface, port)) { xsocket(s, "bind", true /*close*/); }}void bind_socket_range(SOCKET s, IPAddress interface, PortRange const &range){ if (!range.restricted) { // binding to PORT_ANY makes it unrestricted bind_socket(s, interface, PORT_ANY); return; } // pick a port to try; use randomness instead of maintaining state int maxTries = range.high - range.low + 1; int port = range.low + rand() % maxTries; // we will as many attempts as there are ports in the range for (int attempt = 0; attempt < maxTries; attempt++) { // increment to next port // (originally I was picking a random port for each attempt; Dan // suggests that when most ports are used sequential scan may be // better) port++; if (port > range.high) { port = range.low; } if (inner_bind_socket(s, interface, port)) { return; } } // give up xSocket x(s, stringb("Failed to bind any ports in the range [" << range.low << "," << range.high << "] after " << maxTries << " attempts; giving up.")); x.temporary = true; // hopefully THROW(x);} // put a socket into a listening state; 'interface' is passed// so we can print some diagnosticsvoid listen_socket(SOCKET s, IPAddress interface){ if (listen(s, 5 /*backlog queue size*/) == SOCKET_ERROR) { xsocket(s, "listen", true /*close*/); } if (interface == INADDR_ANY) { // can't just print 'port' because it could be PORT_ANY connDiagnostic("listening on port " << getLocalPort(s) << " on all interfaces"); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -