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