這兩天又看了幾眼Socket編程,我老是沒長性,總是東看看西看看。還老愛挑毛病,鉆牛角尖,真是不可救藥了。這不,又開始看Socket不順眼了。當時是看了一個朋友給我的一段socket raw編程的代碼,有一段看的我很惡心。如下:
if((ErrorCode=WSAStartup(MAKEWORD(2,1),&wsaData))!=0){
printf("WSAStartup failed: %d\n",ErrorCode);
return 2;
}
sockMain=WSASocket(AF_INET,SOCK_RAW,IPPROTO_RAW,NULL,0,WSA_FLAG_OVERLAPPED);
if(sockMain==INVALID_SOCKET)
{
printf("Socket failed: %d\n",WSAGetLastError());
return 3;
}
ErrorCode=setsockopt(sockMain,IPPROTO_IP,IP_HDRINCL,(char *)&flag,sizeof(int));
if(ErrorCode==SOCKET_ERROR)
{
printf("Set sockopt failed: %d\n",WSAGetLastError());
return 4;
}
ErrorCode=setsockopt(sockMain,SOL_SOCKET,SO_SNDTIMEO,(char*)&TimeOut,sizeof(TimeOut));
if(ErrorCode==SOCKET_ERROR)
{
printf("Set sockopt time out failed: %d\n",WSAGetLastError());
return 5;
}
也許很多人都覺得我神經,這個不是每個socket程序的必經之路么?要是覺得這個很難受破壞結構,那還怎么寫大型的socket程序?也許吧,我說了我就是一個死腦筋,總喜歡看起來好一些東西??傆X得那種每一個函數調用后,就跟著一堆針對返回值得錯誤處理語句很羅嗦,為什么不用異常來使這些更優雅呢?代碼上的優雅同時也就意味著代碼的出錯幾率的降低。
再加上我對于孟巖他們所探討的C++異常機制的懷疑還是無法深入理解。所以我打算嘗試一下充分使用異常機制來完成類似上述代碼得錯誤處理,用C++封裝Winsock函數庫,以后基于這個自己寫的Socket C++類庫,寫一些通訊程序,從而暴露一些異常機制的弊病,讓自己充分的理解異常機制的一些害處?,F在我的理解還是太膚淺了,不夠深入。
我現在的感覺是網絡通訊中出現的異常簡直太正常了,因為你無法預計將會發生什么,哪里會出錯誤,因此用異常機制去處理時非常合適的。而且我注意到很多地方的錯誤返回,又不少是錯誤發生后,確實沒什么辦法可以恢復的。所以,應該也不違背一些規則。試試看吧。
其實我本來是想找一個STL類似的類庫,結果沒找到。找到一個ACE結果把我給嚇回去了,好大啊。沒必要把。我只需要一個簡單的用C++特性進行封裝的 Socket類庫而已。如果有人聽說了的話,請告訴我,省得我在這里白費力氣。哦,不過也不白費力氣,自己實現一些東西,挺好的。
我自己沒讀過STL代碼,因此無法寫出STL或者Boost風格的代碼來,只能按照自我的想象,反復的調整代碼了。不過通過封裝一個類庫,不斷地debug,不斷地完善,我對C++的理解一定會進一步的。也許那個時候我會說“哦?這是誰寫的垃圾代碼啊,簡直不堪入目,愚蠢至極。。。wait,好像是我寫的”然后就是羞愧至極。呵呵。
目前我實現了一個最簡單的socket類庫,準確說還不能叫做實現了一個類庫,只能叫做用C++封裝了幾個socket函數而已,暫時只能叫做概念試驗,但是我希望將來有一天這個能成長為一個比較簡單、實用的C++ Socket類庫。目前其中有3個類。
class NetException
{
public:
NetException();
NetException(const int ErrorCode);
NetException(const string Message, const int msgId);
virtual ~NetException(void);
string GetErrorMessage(const dword ErrorCode) const;
protected:
string m_Message;
int m_msgId;
public:
string getMessage(void) const
{
return m_Message;
}
int getId(void) const
{
return m_msgId;
}
};
NetException類是作為我所有異常的拋出類,如果有其他庫需要的話,我會將此類作為基類以繼承。
class NetEnviroment
{
public:
NetEnviroment(void);
NetEnviroment(const byte bHighVersion, const byte bLowVersion);
~NetEnviroment(void);
inline word MakeWord(const byte wHigh, const byte wLow)
{
return ((word)wHigh)<<8 | wLow;
}
inline word getVersion() const
{
return m_wsadata.wVersion;
}
inline word getHigherVersion() const
{
return m_wsadata.wHighVersion;
}
inline string getDescription() const
{
return string(m_wsadata.szDescription);
}
inline string getSystemStatus() const
{
return string(m_wsadata.szSystemStatus);
}
protected:
WSADATA m_wsadata;
bool m_bState;
public:
void initial(const byte high, const byte low)
{
if( WSAStartup( MakeWord(high, low), &m_wsadata ) != 0 )
throw NetException();
m_bState = true;
}
};
NetEnviroment是作為整個Socket的環境類。凡是使用網絡的程序,只需要之前放置一個NetEnviroment對象,一切就Ok了,構造函數中調用了WSAStartup。析構函數中會調用WSACleanup()。
class Socket
{
public:
Socket();
Socket(const int protocol, const int type = SOCK_STREAM, const int af = AF_INET);
Socket(const SOCKET s, const int protocol = IPPROTO_TCP, const int type = SOCK_STREAM, const int af = AF_INET);
~Socket(void);
protected:
SOCKET m_socket;
int m_af, m_type, m_protocol, m_port;
SOCKADDR_IN m_remoteaddr;
const static int BUF_SIZE = 4096;
bool m_Connected;
protected:
void initSocket(const int protocol = IPPROTO_TCP, const int type = SOCK_STREAM, const int af = AF_INET);
public:
int Bind(const struct sockaddr* name, const int namelen);
int Bind(const word Port);
int Listen(const int backlog = SOMAXCONN);
Socket Accept(void);
int Connect(const string hostname, const word port);
int Close(void);
int Send(const char* buf, const int len, const int flags = 0);
int Send(const string message, const int flags = 0);
int Receive(char* buf, const int len, const int flags = 0);
string Receive(const int flags = 0);
int SendTo(const char* buf, const int len, const struct sockaddr* to, const int tolen, const int flags = 0);
int ReceiveFrom(char* buf, const int len, struct sockaddr* from, int* fromlen, const int flags = 0);
word getLocalPort(void) const;
struct in_addr getLocalAddress(void) const;
string getLocalAddressString(void) const;
word getRemotePort(void) const;
struct in_addr getRemoteAddress(void) const;
string getRemoteAddressString(void) const;
struct sockaddr_in& getSockName(SOCKET s = NULL) const;
};
ostream& operator << ( ostream& os, const Socket &s );
Socket類是真正干真家伙的類,這里面已經封裝實現了很多基本的Socket函數。并且我的思想是盡量結合STL中的類型。因次我均使用STL中的string和Boost中的Regex。
基于這個類庫,我寫了一個獲取我網站主頁的代碼,應該可以感覺到,代碼異常簡潔。
#include "NetLib.h"
NetEnviroment Env;
int main(int argc, char * argv[])
{
try{
Socket client;
client.Connect("211.157.101.207", 80);
client.Send("GET / HTTP/1.1\r\nHost:www.dancefires.com\r\n\r\n");
cout << client.Receive() << endl;
}catch(NetException nerr){
cerr<< "[" << nerr.getId() << "] " << nerr.getMessage() << endl;
}
}
大家可以看到,用這個類庫實現TCP Client是如此的簡單,Send和Receive都是string(當然我也提供了,原始的[]的接口,呵呵,不然我就無法處理二進制流了。)。
NetLib.h是我做的C++封裝的Socket庫的頭文件。我把NetEnviroment NetEnv放在了全局變量,實際上目的是為了讓程序加載時,自動初始化NetEnv,并且調用WSAStartup(),初始化Socket堆棧。
連接、發送、接收,一氣呵成,看起來確實很簡潔明快。一旦有錯誤發生,便會有異常拋出,并被捕獲,然后將異常的信息輸出出來。這時突然一陣嘈雜的聲音: “嘿!醒醒~~醒醒,別美了~~~這可離你說的Socket類庫可差遠了?!?嗯,以后有時間的話,我會逐步完善。一旦具有雛形,我會放到Open source社區去讓它生長。不過目前,它遠算不上是一個雛形。只能算是想法。還有太多太多的事情要去做……