WSAEventSelect模型

客户端程序:

需要注意的是WSAWaitForMultipleEvents只能支持由WSA_MAXINUM_WAIT_EVENTS对象规定的一个最大值为64,因此最多只能支持64个套接字。

对于写服务端程序而言是一大缺点。

Buffer.h

#pragma once

#include <algorithm>
#include <vector>
#include <iterator>
#include <string.h>
#include <assert.h>

class Buffer
{
public:
  static const size_t kCheapPrepend = 8;
  static const size_t kInitialSize = 1024;

  Buffer()
	  : buffer_(kCheapPrepend + kInitialSize)
	  ,readerIndex_(kCheapPrepend)
	  ,writerIndex_(kCheapPrepend)
	{
		assert(readableBytes() == 0);
		assert(writableBytes() == kInitialSize);
		assert(prependableBytes() == kCheapPrepend);
	}

  void swap(Buffer &rhs)
  {
	  buffer_.swap(rhs.buffer_);
	  std::swap(readerIndex_, rhs.readerIndex_);
	  std::swap(writerIndex_, rhs.writerIndex_);
  }

  size_t readableBytes() const
  {
	  return writerIndex_ - readerIndex_; 
  }

  size_t writableBytes() const
  {
	  return buffer_.size() - writerIndex_;
  }

  size_t prependableBytes() const
  {
	  return readerIndex_;
  }

  const char *peek() const
  {
	  return begin() + readerIndex_;
  }

  void retrieve(size_t len)
  {
	  assert(len <= readableBytes());
	  if ( len < readableBytes())
	  {
		  readerIndex_ += len;
	  }
	  else
	  {
		  retrieveAll();
	  }
  }

  void retrieveUntil(const char *end)
  {
	  assert(peek() <= end);
	  assert(end <= beginWrite());
	  retrieve(end - peek());
  }

  void retrieveInt32()
  {
	  retrieve(sizeof(__int32));
  }

  void retrieveInt16()
  {
	  retrieve(sizeof(__int16));
  }

  void retrieveInt8()
  {
	  retrieve(sizeof(__int8));
  }

  void retrieveAll()
  {
	  readerIndex_ = kCheapPrepend;
	  writerIndex_ = kCheapPrepend;
  }

  std::string retrieveAllAsString()
  {
	  return retrieveAsString(readableBytes());
  }

  std::string retrieveAsString(size_t len)
  {
	  assert(len <= readableBytes());
	  std::string result(peek(), len);
	  retrieve(len);
	  return result;
  }

  void append(const std::string &str)
  {
	  append(str.c_str(), str.size());
  }

  void append(const char* data, size_t len)
  {
	  ensureWritableBytes(len);
	  std::copy(data, data+len, stdext::checked_array_iterator<char*>(beginWrite(), len));
	  //memcpy(beginWrite(), data, len);
	  hasWritten(len);
  }

  void append(const void* data, size_t len)
  {
	  append(static_cast<const char*>(data), len);
  }

  void ensureWritableBytes(size_t len)
  {
	  if (writableBytes() < len)
	  {
		  makeSpace(len);
	  }
	  assert(writableBytes() >= len);
  }

  char* beginWrite()
  {
	  return begin() + writerIndex_;
  }

  const char* beginWrite() const
  {
	  return begin() + writerIndex_;
  }

  void hasWritten(size_t len)
  {
	  writerIndex_ += len;
  }

  void appendInt32(__int32 x)
  {
	  // FIXME network
	  append(&x, sizeof(x));
  }

  void appendInt16(__int16 x)
  {
	  // FIXME network
	  append(&x, sizeof(x));
  }

  void appendInt8(__int8 x)
  {
	  append(&x, sizeof(x));
  }

  // add
  void read(void* data, size_t len)
  {
	  if (data)
	  {
		  memcpy(data, peek(), len);
		  retrieve(len);
	  }
  }

  __int32 readInt32()
  {
	  __int32 result = peekInt32();
	  retrieveInt32();
	  return result;
  }

  __int16 readInt16()
  {
	  __int16 result = peekInt16();
	  retrieveInt16();
	  return result;
  }

  __int8 readInt8()
  {
	  __int8 result = peekInt8();
	  retrieveInt8();
	  return result;
  }

  __int32 peekInt32() const
  {
	  assert(readableBytes() >= sizeof(__int32));
	  __int32 be32 = 0;
	  ::memcpy(&be32, peek(), sizeof(be32));
	  return be32; // FIXME network
  }

  __int16 peekInt16() const
  {
	  assert(readableBytes() >= sizeof(__int16));
	  __int16 be16 = 0;
	  ::memcpy(&be16, peek(), sizeof(be16));
	  return be16; // FIXME network
  }

  __int8 peekInt8() const
  {
	  assert(readableBytes() >= sizeof(__int8));
	  __int8 x = *peek();
	  return x;
  }

  void prependInt32(__int32 x)
  {
	  __int32 be32 = x; // FIXME  network;
	  prepend(&be32, sizeof(be32));
  }

  void prependInt16(__int16 x)
  {
	  __int16 be16 = x; // FIXME network;
	  prepend(&be16, sizeof(be16));
  }

  void prependInt8(__int8 x)
  {
	  prepend(&x, sizeof(x));
  }

  void prepend(const void* data, size_t len)
  {
	  assert(len <= prependableBytes());
	  readerIndex_ -= len;
	  const char *d = static_cast<const char*>(data);
	  std::copy(d, d+len, stdext::checked_array_iterator<char*>(begin()+readerIndex_, len));
	  //memcpy(begin()+readerIndex_, d, len);
  }

private:
	char *begin()
	{
		return &*buffer_.begin();
	}

	const char* begin() const
	{
		return &*buffer_.begin();
	}

	void makeSpace(size_t len)
	{
		if (writableBytes() + prependableBytes() < len + kCheapPrepend)
		{
			buffer_.resize(writerIndex_ + len);
		}
		else
		{
			assert(kCheapPrepend < readerIndex_);
			size_t readable = readableBytes();
			std::copy(begin() + readerIndex_,
				begin() + writerIndex_,
				stdext::checked_array_iterator<char*>(begin()+readerIndex_, writerIndex_-readerIndex_));
			//memcpy(begin()+kCheapPrepend, begin()+readerIndex_, writerIndex_-readerIndex_);
			readerIndex_ = kCheapPrepend;
			writerIndex_ = readerIndex_ + readable;
			assert(readable == readableBytes());
		}
	}

	Buffer(const Buffer&);
	const Buffer& operator=(const Buffer&);

private:
	std::vector<char> buffer_;
	size_t readerIndex_;
	size_t writerIndex_;
};

MutexLock.h

#include <Windows.h>

class MutexLock
{
public:
	MutexLock()
	{
		InitializeCriticalSection(&criticalSection_);
	}

	~MutexLock()
	{
		DeleteCriticalSection(&criticalSection_);
	}

	void lock()
	{
		EnterCriticalSection(&criticalSection_);
	}

	void unlock()
	{
		LeaveCriticalSection(&criticalSection_);
	}

private:
	MutexLock(const MutexLock&);
	MutexLock& operator=(const MutexLock&);

	CRITICAL_SECTION criticalSection_;
};

class MutexLockGuard
{
public:
	explicit MutexLockGuard(MutexLock& mutex)
		: mutex_(mutex)
	{
		mutex_.lock();
	}

	~MutexLockGuard()
	{
		mutex_.unlock();
	}

private:
	MutexLockGuard(const MutexLockGuard&);
	MutexLockGuard operator=(const MutexLockGuard&);

	MutexLock& mutex_;
};

#define MutexLockGuard(x) assert(!"Missing guard object name")

EventSelect.h

#pragma once

#include <iostream>
#include <WinSock2.h>
#include <string>
#include <process.h>
#include <memory>
#include "Buffer.h"
#include "MutexLock.h"

class TcpClient
{
public:
	TcpClient(const std::string &ip, unsigned short port);
	virtual ~TcpClient();
	int start();
	void stop();
	int sendMessage(const Buffer &val);
	virtual int onMessage(Buffer &val);
	virtual void onClose();

private:
	TcpClient(const TcpClient&);
	TcpClient operator= (const TcpClient&);

	static unsigned int __stdcall selectEventProc(void *arg);
	bool connect();
	void createEvent();
	void closeEvent();
	bool selectEventHandle();
	bool sendFully();

private:
	bool isConnected_;
	std::string ip_;
	unsigned short port_;
	unsigned int curThreadId_;
	SOCKET serverSock_;
	WSAEVENT selectEvent_;
	WSAEVENT sendEvent_;
	WSAEVENT stopEvent_;
	HANDLE thread_;
	Buffer sendBuffer_;
	Buffer recvBuffer_;
	MutexLock sendMutex_;
};
// EventSelect.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <WinSock2.h>
#include <string>
#include <process.h>
#include <memory>
#include <assert.h>
#include "EventSelect.h"
#include "Buffer.h"

#pragma comment(lib, "Ws2_32.lib")

TcpClient::TcpClient(const std::string &ip, unsigned short port)
	: isConnected_(false)
	, ip_(ip)
	, port_(port)
	, serverSock_(INVALID_SOCKET)
	, selectEvent_(WSA_INVALID_EVENT)
	, sendEvent_(WSA_INVALID_EVENT)
	, stopEvent_(WSA_INVALID_EVENT)
{
}

TcpClient::~TcpClient()
{
	stop();
}

std::string GetLocalIp(SOCKET sock)
{
	std::string ret;
	sockaddr_in localAddr;
	int addrLen = sizeof(localAddr);
	if (0 == getsockname(sock, (sockaddr*)&localAddr, &addrLen))
	{
		ret = inet_ntoa(localAddr.sin_addr);
	}
	return ret;
}

int TcpClient::start()
{
	if (connect())
	{
		isConnected_ = true;
		thread_ = (HANDLE)_beginthreadex(NULL, 0, selectEventProc, this, 0, &curThreadId_);
		return 0;
	}
	else
	{
		return WSAGetLastError();
	}
}

void TcpClient::stop()
{
	if (isConnected_)
	{
		if (stopEvent_)
		{
			WSASetEvent(stopEvent_);
		}

		if (::GetCurrentThreadId() != curThreadId_)
		{
			WaitForSingleObject(thread_, INFINITE);
		}
		::CloseHandle(thread_);

		if (INVALID_SOCKET != serverSock_)
		{
			shutdown(serverSock_, SD_BOTH);
			closesocket(serverSock_);
			serverSock_ = INVALID_SOCKET;
		}

		closeEvent();
		isConnected_ = false;
		fprintf(stdout, "bye bye...\n");
	}
}

int TcpClient::sendMessage(const Buffer &val)
{
	if (isConnected_)
	{
		MutexLockGuard lock(sendMutex_);
		sendBuffer_.append(val.peek(), val.readableBytes());
		WSASetEvent(sendEvent_);
		return 0;
	}
	return -1;
}

int TcpClient::onMessage(Buffer &val)
{
	return 0;
}

void TcpClient::onClose()
{
}

unsigned int __stdcall TcpClient::selectEventProc(void *arg)
{
	TcpClient *pSelf = (TcpClient*)arg;
	assert(NULL != pSelf);

	HANDLE hEvents[] = {pSelf->selectEvent_, pSelf->sendEvent_, pSelf->stopEvent_};
	while (pSelf->isConnected_)
	{
		fprintf(stdout, "loop...\n");
		unsigned int index = WSAWaitForMultipleEvents(3, hEvents, FALSE, WSA_INFINITE, FALSE);
		if (index == WSA_WAIT_EVENT_0)
		{
			fprintf(stdout, "select event...\n");
			if (!pSelf->selectEventHandle())
			{
				break;
			}
		}
		else if (index == WSA_WAIT_EVENT_0 + 1)
		{
			fprintf(stdout, "send event...\n");
			if (!pSelf->sendFully())
			{
				break;
			}
		}
		else if (index == WSA_WAIT_EVENT_0 + 2)
		{
			fprintf(stdout, "stop event...\n");
			break;
		}
		else if (index == WSA_WAIT_FAILED)
		{
			break;
		}
		else
		{
			assert(false);
		}
		if (index != WSA_WAIT_EVENT_0)		
			WSAResetEvent(hEvents[index - WSA_WAIT_EVENT_0]);
	}
	
	return 0;
}

bool TcpClient::connect()
{
	serverSock_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET != serverSock_)
	{
		sockaddr_in sin;
		memset(&sin, 0, sizeof(sin));
		sin.sin_family = AF_INET;
		sin.sin_addr.S_un.S_addr = inet_addr(ip_.c_str());
		sin.sin_port = htons(port_);
		if (SOCKET_ERROR != ::connect(serverSock_, (sockaddr*)&sin, sizeof(sin)))
		{
			createEvent();
			int ret = WSAEventSelect(serverSock_, selectEvent_, FD_READ | FD_WRITE | FD_CLOSE);
			if (SOCKET_ERROR != ret)
			{
				return true;
			}
			else
			{
				closeEvent();
			}
		}
		else
		{
			closesocket(serverSock_);
		}
	}
	return false;
}

void TcpClient::createEvent()
{
	selectEvent_ = WSACreateEvent();
	sendEvent_ = WSACreateEvent();
	stopEvent_ = WSACreateEvent();
	assert(WSA_INVALID_EVENT != selectEvent_);
	assert(WSA_INVALID_EVENT != sendEvent_);
	assert(WSA_INVALID_EVENT != stopEvent_);
}

void TcpClient::closeEvent()
{
	if (WSA_INVALID_EVENT != selectEvent_)
	{
		WSACloseEvent(selectEvent_);
		selectEvent_ = WSA_INVALID_EVENT;
	}

	if (WSA_INVALID_EVENT != sendEvent_)
	{
		WSACloseEvent(sendEvent_);
		sendEvent_ = WSA_INVALID_EVENT;
	}

	if (WSA_INVALID_EVENT != stopEvent_)
	{
		WSACloseEvent(stopEvent_);
		stopEvent_ = WSA_INVALID_EVENT;
	}
}

bool TcpClient::selectEventHandle()
{
	WSANETWORKEVENTS curEvent;
	int ret = WSAEnumNetworkEvents(serverSock_, selectEvent_, &curEvent);
	if (SOCKET_ERROR == ret)
	{
		WSAResetEvent(selectEvent_);
		return false;
	}
	else
	{
		if (curEvent.lNetworkEvents & FD_READ)
		{
			if (0 == curEvent.iErrorCode[FD_READ_BIT])
			{
				printf("read...\n");
				char buffer[65536];
				ret = ::recv(serverSock_, buffer, sizeof(buffer), 0);
				if (ret > 0)
				{
					recvBuffer_.append(buffer, ret);
					onMessage(recvBuffer_);
				}
				else
				{
					return false;
				}
			}
			else
			{
				return false;
			}
		}

		if (curEvent.lNetworkEvents & FD_WRITE)
		{
			printf("write...\n");
			if (0 == curEvent.iErrorCode[FD_WRITE_BIT])
			{
				if (!sendFully())
				{
					return false;
				}
			}
			else
			{
				return false;
			}
		}

		if (curEvent.lNetworkEvents & FD_CLOSE)
		{
			if (0 == curEvent.iErrorCode[FD_CLOSE_BIT])
			{
				onClose();
			}
			return false;
		}
	}

	return true;
}

bool TcpClient::sendFully()
{
	MutexLockGuard lock(sendMutex_);
	unsigned int left = sendBuffer_.readableBytes();
	while (left > 0)
	{
		int sendNum = ::send(serverSock_, sendBuffer_.peek(), left, 0);
		if (SOCKET_ERROR == sendNum)
		{
			int lastError = WSAGetLastError();
			if (WSAEWOULDBLOCK == lastError)
			{
				continue;
			}
			else
			{
				return false;
			}
		}
		left -= sendNum;
		sendBuffer_.retrieve(sendNum);
	}
	return true;
}



main.cpp
#include "stdafx.h"
#include "EventSelect.h"
#include <memory>
#include <functional>
#include <time.h>
#include "Buffer.h"

// 产生随机字符串
std::string BuildRandString(int num)
{
	static unsigned int s_add = 0;
	std::string ret;
	srand((unsigned int)time(NULL) + (s_add++));
	for (int i=0; i<num; )
	{
		char buf[17] = {0};
		_itoa_s(rand(), buf, 0x10);
		ret += buf;
		i += strlen(buf);
	}
	return ret.substr(0, num);
}

class MyClient : public TcpClient
{
public:
	MyClient(const std::string &ip, unsigned short port)
		: TcpClient(ip, port)
	{
	}

	virtual int onMessage(Buffer &val)
	{
		printf("recv:%s\n", val.retrieveAllAsString().c_str());
		return 0;
	}

	virtual void onClose()
	{
		printf("server is closed\n");
	}
};

unsigned int __stdcall CreateClient(void *)
{
	std::tr1::shared_ptr<TcpClient> clientPtr(new MyClient("127.0.0.1", 5100));
	int ret = clientPtr->start();
	::Sleep(1000);

	while (0 == ret)
	{
		::Sleep(100);
		std::string strRand = BuildRandString(500);
		DWORD id = ::GetCurrentThreadId();
		char buf[16] = {0};
		sprintf_s(buf, "%05d", id);
		std::string strSend = std::string("[") + std::string(buf) + std::string("]") + strRand;
		Buffer sendBuf;
		sendBuf.append(strSend.c_str(), strSend.size());
		clientPtr->sendMessage(sendBuf);
	}

	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	std::string input;
	std::cout << "create client num:" << std::endl;
	std::cin >> input;
	int clientNum = atoi(input.c_str());
	HANDLE *pHandle = new HANDLE[clientNum];

	for (int i=0; i<clientNum; i++)
	{
		pHandle[i] = (HANDLE)_beginthreadex(NULL, 0, CreateClient, NULL, 0, NULL);
	}

	WaitForMultipleObjects(clientNum, pHandle, TRUE, INFINITE);
	WSACleanup();

	system("pause");
	return 0;
}
























评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值