Win32下使用Socket:WinSock

学习socket最好能有两台以上联网的电脑,以及能获得公网IP的网络接入方式。两年前,我主要使用的是一台win2k3和Debain Linux双系统的电脑,例外有台99年的老机器装着win98,而且没有装VC,测试相当的麻烦。现在买了笔记本,使用的是Vista的win32环境(32位),可以直接和老电脑的Linux联网进行测试。另外,网络环境也换成了电信的ADSL,贵了很多,为的就是能有一个公网IP。接下来的教程我会兼顾winsock的代码,这主要是因为winsock本身对socket几乎是兼容的。所以,这里有必要先说明在VC环境中使用socket的一些简单设置,以及与Linux环境下的细微差别。
我的VC环境依然是2008 Express,在写这篇教程的时候,微软已经发布了VC 2010,目前在微软的官方主页,提供了VC 2010的下载,同时保留着VC 2008的下载。
我们在VC中建立一个控制台的空项目:
Win32下使用Socket:WinSock_第1张图片
Win32下使用Socket:WinSock_第2张图片
Win32下使用Socket:WinSock_第3张图片
我们着手构建自己的第一个winsock程序。
首先win32下与Linux下的socket API需要包含不同的头文件。
在Linux下是这些:

#include  < unistd.h >
#include 
< sys / socket.h >
#include 
< arpa / inet.h >
win32下的winsock有多个版本,我所找到的资料中,老的版本是:
#include  < winsock.h >
与之对应的需要的链接库为:
Win32下使用Socket:WinSock_第4张图片

Win32下使用Socket:WinSock_第5张图片
这可能可以兼容非常古老的版本中的winsock,比如win98,而微软官方所推荐的是:
#include  < winsock2.h >
链接库是:ws2_32.lib,这样就可以使用高版本的winsock。
那么,什么是winsock的版本?这就涉及到winsock的初始化函数WSAStartup:
http://msdn.microsoft.com/en-us/library/ms742213(v=VS.85).aspx
上面是微软的官方说明,我这里构建一个简单的类,希望每次使用的时候引入一个类对象就可以了。
class  WinsockAPI{
private :
    WSADATA wsaData;
public :
    WinsockAPI(
int  low_byte  =   2 int  high_byte  =   2 );
    
~ WinsockAPI();
    
void  showVersion()  const ;
};
WSADATA是记录着winsock信息的结构。
// class WinsockAPI

WinsockAPI::WinsockAPI(
int  low_byte,  int  high_byte)
{
    
const  WORD wVersionRequested  =  MAKEWORD(low_byte, high_byte);
    
int  wsa_startup_err  =  WSAStartup(wVersionRequested,  & wsaData);
    
if  (wsa_startup_err  !=   0 ) {
        std::cerr 
<<   " WSAStartup() failed. "   <<  std::endl;
        
throw  wsa_startup_err;
    }
}

WinsockAPI::
~ WinsockAPI()
{
    WSACleanup();
}

void  WinsockAPI::showVersion()  const
{
    std::cout    
<<   " The version of Winsock.dll is  "  
                
<<   int (LOBYTE(wsaData.wVersion)) 
                
<<   " . "  
                
<<   int (HIBYTE(wsaData.wVersion)) 
                
<<   " . "  
                
<<  std::endl;
    
return ;
}

首先,宏MAKEWORD()将两个int转换为winsock形式的版本号,我这里默认是是2.2,就只需要MAKEWORD(2, 2),如果是老版本的,最低应该是1.0。WSAStartup()将winsock的初始化信息写入一个WSADATA结构(我们这里的wsaData),如果成功返回0,失败将返回一个int的错误代码。这个错误代码直接表示了错误信息,微软官方建议不使用winsock的通用异常信息获取函数WSAGetLastError()获取WSAStartup()的错误信息,这可能是因为如果WSAStartup()失败,那么winsock的错误信息不一定能够正确的构建出来的缘故。
最后,winsock结束后用WSACleanup()清理。
因为socket本身的复杂性,异常信息提示非常重要。WSAGetLastError()的官方说明如下:
http://msdn.microsoft.com/en-us/library/ms741580(VS.85).aspx
错误代码所反馈的信息查询在这里:
http://msdn.microsoft.com/en-us/library/ms740668(v=VS.85).aspx
最后,需要注意的问题是,因为socket是构建在UNIX系统下的(BSD socket是当今所有socket的基础),所以socket很好的利用了UNIX体系“一切都是文件”的性质,每个socket本身也就是一个UNIX文件描述符,因此,Linux下的socket是用关闭文件的函数close()关闭的。但是win32下没这个性质,所以winsock是另外一种抽象,但是好在同样用int作为描述符,关闭需要专门为winsock定做的函数closesocket()。
下面重写了TCP Server的代码(类的抽象和构造也重新写了,将在下一章解释),作为winsock使用的演示。

 

// Filename: SockClass.hpp

#ifndef SOCK_CLASS_HPP
#define  SOCK_CLASS_HPP

#include 
< iostream >
#include 
< winsock2.h >

namespace  sockClass
{
void  error_info( const   char *  s);
}

class  WinsockAPI{
private :
    WSADATA wsaData;
public :
    WinsockAPI(
int  low_byte  =   2 int  high_byte  =   2 );
    
~ WinsockAPI();
    
void  showVersion()  const ;
};

class  BaseSock{
protected :
    
int  sockFD;
public :
    BaseSock();
    
virtual   ~ BaseSock()  =   0 ;
    
const   int &  showSockFD()  const ;
};

class  TCPListenSock:  public  BaseSock{
private :
    sockaddr_in listenSockAddr;
public :
    TCPListenSock(unsigned 
short  listen_port);
    
~ TCPListenSock();
    
void  TCPListen(
        
int  max_connection_requests  =   10 const ;
};

class  TCPServerSock:  public  BaseSock{
private :
    sockaddr_in clientSockAddr;
protected :
    
char *  preBuffer;
    
int  preBufferSize;
    mutable 
int  preReceivedLength;
public :
    TCPServerSock(
        
const  TCPListenSock &  listen_sock,
        
int  pre_buffer_size  =   32 );
    
virtual   ~ TCPServerSock();
    
int  TCPReceive()  const ;
    
int  TCPSend( const   char *  send_data,
            
const   int &  data_length)  const ;
};

#endif   // SockClass.hpp

// Filename: SockClass.cpp

#include 
" SockClass.hpp "

// sockClass

namespace  sockClass
{
void  error_info( const   char *  s)
{
    std::cerr 
<<  s  <<  std::endl;
    
throw  WSAGetLastError();
}
}

// class WinsockAPI

WinsockAPI::WinsockAPI(
int  low_byte,  int  high_byte)
{
    
const  WORD wVersionRequested  =  MAKEWORD(low_byte, high_byte);
    
int  wsa_startup_err  =  WSAStartup(wVersionRequested,  & wsaData);
    
if  (wsa_startup_err  !=   0 ) {
        std::cerr 
<<   " WSAStartup() failed. "   <<  std::endl;
        
throw  wsa_startup_err;
    }
}

WinsockAPI::
~ WinsockAPI()
{
    WSACleanup();
}

void  WinsockAPI::showVersion()  const
{
    std::cout    
<<   " The version of Winsock.dll is  "  
                
<<   int (LOBYTE(wsaData.wVersion)) 
                
<<   " . "  
                
<<   int (HIBYTE(wsaData.wVersion)) 
                
<<   " . "  
                
<<  std::endl;
    
return ;
}

// class BaseSock

BaseSock::BaseSock():
sockFD(
- 1 )
{}

BaseSock::
~ BaseSock()
{}

const   int &  BaseSock::showSockFD()  const
{
    
return  sockFD;
}

// class TCPListenSock

TCPListenSock::TCPListenSock(unsigned 
short  listen_port)
{
    sockFD 
=  socket(PF_INET,
                    SOCK_STREAM,
                    IPPROTO_TCP);
    
if  (sockFD  <   0 ) {
        sockClass::error_info(
" socket() failed. " );
    }
    memset(
& listenSockAddr,  0 sizeof (listenSockAddr));
    listenSockAddr.sin_family 
=  AF_INET;
    listenSockAddr.sin_addr.s_addr 
=  htonl(INADDR_ANY);
    listenSockAddr.sin_port 
=  htons(listen_port);
    
if  (bind(    sockFD,
                (sockaddr
* ) & listenSockAddr,
                
sizeof (listenSockAddr))  <   0 ) {
        sockClass::error_info(
" bind() failed. " );
    }
}

TCPListenSock::
~ TCPListenSock()
{
    closesocket(sockFD);
}

void  TCPListenSock::TCPListen(
                        
int  max_connection_requests)  const
{
    
if  (listen(    sockFD,
                max_connection_requests) 
<   0 ) {
        sockClass::error_info(
" listen() failed. " );
    }
}

// class TCPServerSock

TCPServerSock::TCPServerSock(
                
const  TCPListenSock &  listen_sock,
                
int  pre_buffer_size):
preBufferSize(pre_buffer_size),
preReceivedLength(
0 )
{
    preBuffer 
=   new   char [preBufferSize];

    
int  clientSockAddrLen  =   sizeof (clientSockAddr);
    sockFD 
=  accept(    listen_sock.showSockFD(),
                        (sockaddr
* ) & clientSockAddr,
                        
& clientSockAddrLen);
    
if  (sockFD  <   0 ) {
        sockClass::error_info(
" accept() failed. " );
    }
    std::cout    
<<   " Client (IP:  "
                
<<  inet_ntoa(clientSockAddr.sin_addr)
                
<<   " ) conneted. "   <<  std::endl;
}

TCPServerSock::
~ TCPServerSock()
{
    delete [] preBuffer;
    closesocket(sockFD);
}

int  TCPServerSock::TCPReceive()  const
{
    preReceivedLength 
=  recv(    sockFD,
                                preBuffer,
                                preBufferSize,
                                
0 );
    
if  (preReceivedLength  <   0 ) {
        sockClass::error_info(
" recv() failed. " );
    } 
else   if  (preReceivedLength  ==   0 ) {
        std::cout 
<<   " Client has been disconnected./n " ;
        
return   0 ;
    }
    
return  preReceivedLength;
}

int  TCPServerSock::TCPSend( const   char *  send_data,
                           
const   int &  data_length)  const
{
    
if  (data_length  >  preBufferSize) {
        
throw   " Data is too large, resize preBufferSize. " ;
    }

    
int  sent_length  =  send(    sockFD,
                            send_data,
                            data_length,
                            
0 );
    
if  (sent_length  <   0 ) {
        sockClass::error_info(
" send() failed. " );
    } 
else   if  (sent_length  !=  data_length) {
        sockClass::error_info(
" sent unexpected number of bytes. " );
    }

    
return  sent_length;
}

// Filename AppSock.hpp

#ifndef APP_SOCK_HPP
#define  APP_SOCK_HPP

#include 
" SockClass.hpp "

class  TCPEchoServer:  public  TCPServerSock{
public :
    TCPEchoServer(
        
const  TCPListenSock &  listen_sock,
        
int  pre_buffer_size  =   32 );
    
~ TCPEchoServer();
    
bool  handEcho()  const ;
};

#endif   // AppSock.hpp

// Filename: AppSock.cpp

#include 
< string >
#include 
" AppSock.hpp "

TCPEchoServer::TCPEchoServer(
const  TCPListenSock &  listen_sock,  int  pre_buffer_size):
TCPServerSock(listen_sock, pre_buffer_size)
{}

TCPEchoServer::
~ TCPEchoServer()
{}

bool  TCPEchoServer::handEcho()  const
{
    
const  std:: string  SHUTDOWN_CMD  =   " /shutdown " ;
    
while  (TCPReceive()  >   0 ) {
        std::
string  cmd(preBuffer, SHUTDOWN_CMD.size());
        
if  (cmd  ==  SHUTDOWN_CMD  &&  preReceivedLength  ==  SHUTDOWN_CMD.size()) {
            
return   false ;
        }
        TCPSend(preBuffer, preReceivedLength);
    }
    
return   true ;
}

// Filename: main.cpp

#include 
" SockClass.hpp "
#include 
" AppSock.hpp "

int  TCP_echo_server( int  argc,  char *  argv[]);

int  main( int  argc,  char *  argv[])
{
    
int  mainRtn  =   0 ;
    
try  {
        mainRtn 
= TCP_echo_server(argc, argv);
    }
    
catch  ( const   char *  s) {
        perror(s);
        
return   1 ;
    }
    
catch  ( const   int &  err) {
        std::cerr 
<<   " Error:  "   <<  err  <<  std::endl;
        
return   1 ;
    }

    
return  mainRtn;
}

int  TCP_echo_server( int  argc,  char *  argv[])
{
    
const  unsigned  short  DEFAULT_PORT  =   5000 ;
    unsigned 
short  listen_port  =  DEFAULT_PORT;
    
if  (argc  ==   2   &&  atoi(argv[ 1 ])  >   0 ) {
        listen_port 
=  atoi(argv[ 1 ]);
    }

    WinsockAPI winsockInfo;
    winsockInfo.showVersion();

    TCPListenSock listen_sock(listen_port);
    listen_sock.TCPListen();

    
bool  go_on  =   true ;
    
while  (go_on){
        TCPEchoServer echo_server(listen_sock);
        go_on 
=  echo_server.handEcho();
    }

    
return   0 ;
}

你可能感兴趣的:(linux,socket,server,unix,buffer,byte)