⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 socket.cpp

📁 软件是使用VC70
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/* 
 * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "stdinc.h"
#include "DCPlusPlus.h"

#include "Socket.h"

#include "SettingsManager.h"
#include "ResourceManager.h"

#include "ServerSocket.h"

string Socket::udpServer;
short Socket::udpPort;

#define checkconnected() if(!isConnected()) throw SocketException(STRING(NOT_CONNECTED))

#ifdef _DEBUG

SocketException::SocketException(int aError) throw() {
	error = "SocketException: " + errorToString(aError);
	dcdebug("Thrown: %s\n", error.c_str());
}

#else // _DEBUG

SocketException::SocketException(int aError) throw() {
	error = errorToString(aError);
}

#endif

Socket::Stats Socket::stats = { 0, 0 };

string SocketException::errorToString(int aError) throw() {
	switch(aError) {
	case EWOULDBLOCK:
		return STRING(OPERATION_WOULD_BLOCK_EXECUTION);
	case EACCES:
		return STRING(PERMISSION_DENIED);
	case EADDRINUSE:
		return STRING(ADDRESS_ALREADY_IN_USE);
	case EADDRNOTAVAIL:
		return STRING(ADDRESS_NOT_AVAILABLE);
	case EALREADY:
		return STRING(NON_BLOCKING_OPERATION);
	case ECONNREFUSED:
		return STRING(CONNECTION_REFUSED);
	case ETIMEDOUT:
		return STRING(CONNECTION_TIMEOUT);
	case EHOSTUNREACH:
		return STRING(HOST_UNREACHABLE);
	case ESHUTDOWN:
		return STRING(SOCKET_SHUT_DOWN);
	case ECONNABORTED:
		return STRING(CONNECTION_CLOSED);
	case ECONNRESET:
		return STRING(CONNECTION_RESET);
	case ENOTSOCK:
		return STRING(NOT_SOCKET);
	case ENOTCONN:
		return STRING(NOT_CONNECTED);
	case ENETUNREACH:
		return STRING(NETWORK_UNREACHABLE);
	default:
		{
			char tmp[64];
			sprintf(tmp, CSTRING(UNKNOWN_ERROR), aError);
			return tmp;
		}
	}
}

void Socket::create(int aType /* = TYPE_TCP */, bool server /* = false */) throw(SocketException) {
	if(sock != INVALID_SOCKET)
		Socket::disconnect();

	switch(aType) {
	case TYPE_TCP:
		checksocket(sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
		break;
	case TYPE_UDP:
		checksocket(sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP));
		break;
	default:
		dcasserta(0);
	}
	// Multiple interface fix
	if(!server && SETTING(BIND_ADDRESS) != "0.0.0.0") {
		sockaddr_in sock_addr;
		sock_addr.sin_family = AF_INET;
		sock_addr.sin_port = htons(0); // Let stack choose our port
		sock_addr.sin_addr.s_addr = inet_addr(SETTING(BIND_ADDRESS).c_str());
		::bind(sock, (sockaddr *)&sock_addr, sizeof(sock_addr));
		// If it fails, we'll get normal INADDR binding instead...
	}	
	type = aType;
}

/**
 * Binds an UDP socket to a certain local port.
 */
void Socket::bind(short aPort) throw (SocketException){
	dcassert(type == TYPE_UDP);

	sockaddr_in sock_addr;
		
	sock_addr.sin_family = AF_INET;
	sock_addr.sin_port = htons(aPort);
	// Multiple interface fix
	sock_addr.sin_addr.s_addr = inet_addr(SETTING(BIND_ADDRESS).c_str());
	if(::bind(sock, (sockaddr *)&sock_addr, sizeof(sock_addr)) == SOCKET_ERROR) {
	sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    checksockerr(::bind(sock, (sockaddr *)&sock_addr, sizeof(sock_addr)));
	}
	connected = true;
}

void Socket::accept(const ServerSocket& aSocket) throw(SocketException){
	if(sock != INVALID_SOCKET) {
		Socket::disconnect();
	}
	type = TYPE_TCP;
	dcassert(!isConnected());

	sockaddr_in sock_addr;
	socklen_t sz = sizeof(sock_addr);

	checksockerr(sock=::accept(aSocket.getSocket(), (sockaddr*)&sock_addr, &sz));
#ifdef _WIN32
	// Make sure we disable any inherited windows message things for this socket.
	::WSAAsyncSelect(sock, NULL, 0, 0);
#endif
	setBlocking(true);
	connected = true;
	
	setIp(inet_ntoa(sock_addr.sin_addr));
}

/**
 * Connects a socket to an address/ip, closing any other connections made with
 * this instance.
 * @param aAddr Server address, in dns or xxx.xxx.xxx.xxx format.
 * @param aPort Server port.
 * @throw SocketException If any connection error occurs.
 */
void Socket::connect(const string& aAddr, short aPort) throw(SocketException) {
	sockaddr_in  serv_addr;
	hostent* host;

	if(sock == INVALID_SOCKET) {
		create();
	}

	memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_port = htons(aPort);
	serv_addr.sin_family = AF_INET;
	
	serv_addr.sin_addr.s_addr = inet_addr(aAddr.c_str());

    if (serv_addr.sin_addr.s_addr == INADDR_NONE) {   /* server address is a name or invalid */
        host = gethostbyname(aAddr.c_str());
        if (host == NULL) {
            throw SocketException(STRING(UNKNOWN_ADDRESS));
        }
        serv_addr.sin_addr.s_addr = *((u_int32_t*)host->h_addr);
    }

	setIp(inet_ntoa(serv_addr.sin_addr));

    if(::connect(sock,(sockaddr*)&serv_addr,sizeof(serv_addr)) == SOCKET_ERROR) {
		// EWOULDBLOCK is ok, the attempt is still being made, and FD_CONNECT will be signaled...
		if(errno != EWOULDBLOCK) {
			checksockerr(SOCKET_ERROR);
		}
	}
	connected = true;
}

/**
 * Reads zero to aBufLen characters from this socket, 
 * @param aBuffer A buffer to store the data in.
 * @param aBufLen Size of the buffer.
 * @return Number of bytes read, 0 if disconnected and -1 if the call would block.
 * @throw SocketException On any failure.
 */
int Socket::read(void* aBuffer, int aBufLen) throw(SocketException) {
	checkconnected();
	int len = 0;
	if(type == TYPE_TCP) {
		checkrecv(len=::recv(sock, (char*)aBuffer, aBufLen, 0));
	} else if(type == TYPE_UDP) {
		checkrecv(len=::recvfrom(sock, (char*)aBuffer, aBufLen, 0, NULL, NULL));
	}
	stats.totalDown += len;
	return len;
}

/**
 * Reads zero to aBufLen characters from this socket, 
 * @param aBuffer A buffer to store the data in.
 * @param aBufLen Size of the buffer.
 * @param aIP Remote IP address
 * @return Number of bytes read, 0 if disconnected and -1 if the call would block.
 * @throw SocketException On any failure.
 */
int Socket::read(void* aBuffer, int aBufLen, string &aIP) throw(SocketException) {
	checkconnected();
	int len = 0;

	sockaddr_in remote_addr = { 0 };
	socklen_t addr_length = sizeof(remote_addr);

	checkrecv(len=::recvfrom(sock, (char*)aBuffer, aBufLen, 0, (sockaddr*)&remote_addr, &addr_length)); //
	aIP = string(inet_ntoa(remote_addr.sin_addr));

	stats.totalDown += len;
	return len;
}

/**
 * Reads data until aBufLen bytes have been read or an error occurs.
 * On error, an unspecified amount of bytes might have already been read...
 */
int Socket::readFull(void* aBuffer, int aBufLen) throw(SocketException) {
	int i = 0;
	int j;
	while(i < aBufLen) {
		if((j = read(((char*)aBuffer) + i, aBufLen - i)) <= 0) {
			return j;
		}
		i += j;
	}
	return i;
}

/**
 * Sends data, will block until all data has been sent or an exception occurs
 * @param aBuffer Buffer with data
 * @param aLen Data length
 * @throw SocketExcpetion Send failed.
 */
void Socket::write(const char* aBuffer, size_t aLen) throw(SocketException) {
	checkconnected();
//	dcdebug("Writing %db: %.100s\n", aLen, aBuffer);
        
        if(aLen == 0){
                return;
        }

	size_t pos = 0;
	size_t sendSize = min(aLen, (size_t)64 * 1024);

	bool blockAgain = false;

	while(pos < aLen) {
		int i = ::send(sock, aBuffer+pos, (int)min(aLen-pos, sendSize), 0);
		if(i == SOCKET_ERROR) {
			if(errno == EWOULDBLOCK) {
				if(blockAgain) {
					// Uhm, two blocks in a row...try making the send window smaller...
					if(sendSize >= 256) {
						sendSize /= 2;
						dcdebug("Reducing send window size to %lu\n", sendSize);
					} else {
						Thread::sleep(10);
					}
					blockAgain = false;
				} else {
					blockAgain = true;
				}
				wait(2000, WAIT_WRITE);

			} else if(errno == ENOBUFS) {
				if(sendSize > 32) {
					sendSize /= 2;
					dcdebug("Reducing send window size to %lu\n", sendSize);
				} else {
					throw SocketException(STRING(OUT_OF_BUFFER_SPACE));

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -