客户端程序:
需要注意的是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;
}