socket
我们采用winsock作为网络部分的编程接口,
接下去编程者有必要学习一下socket的基本知识,
不过不懂也没有关系,我提供的代码已经把那些麻烦的细节或者正确的系统设置给弄好了,
编程者只需要按照规则编写游戏系统的处理代码就可以了,
这些代码在vc6下编译通过,
是通用的网络传输底层,
这里是socket部分的代码,
我们需要安装vc6才能够编译以下的代码,
因为接下去我们要接触越来越多的c++,
所以,大家还是去看看c++的书吧,
// socket.h
#ifndef _socket_h
#define _socket_h
#pragma once
//定义最大连接用户数目 ( 最大支持 512 个客户连接 )
#define MAX_CLIENTS 512
//#define FD_SETSIZE MAX_CLIENTS
#pragma comment( lib, "wsock32.lib" )
#include <winsock.h>
class CSocketCtrl
{
void SetDefaultOpt();
public:
CSocketCtrl(): m_sockfd(INVALID_SOCKET){}
BOOL StartUp();
BOOL ShutDown();
BOOL IsIPsChange();
BOOL CanWrite();
BOOL HasData();
int Recv( char* pBuffer, int nSize, int nFlag );
int Send( char* pBuffer, int nSize, int nFlag );
BOOL Create( UINT uPort );
BOOL Create(void);
BOOL Connect( LPCTSTR lpszHostAddress, UINT nHostPort );
void Close();
BOOL Listen( int nBackLog );
BOOL Accept( CSocketCtrl& sockCtrl );
BOOL RecvMsg( char *sBuf );
int SendMsg( char *sBuf,unsigned short stSize );
SOCKET GetSockfd(){ return m_sockfd; }
BOOL GetHostName( char szHostName[], int nNameLength );
protected:
SOCKET m_sockfd;
static DWORD m_dwConnectOut;
static DWORD m_dwReadOut;
static DWORD m_dwWriteOut;
static DWORD m_dwAcceptOut;
static DWORD m_dwReadByte;
static DWORD m_dwWriteByte;
};
#endif
// socket.cpp
#include <stdio.h>
#include "msgdef.h"
#include "socket.h"
// 吊线时间
#define ALL_TIMEOUT 120000
DWORD CSocketCtrl::m_dwConnectOut = 60000;
DWORD CSocketCtrl::m_dwReadOut = ALL_TIMEOUT;
DWORD CSocketCtrl::m_dwWriteOut = ALL_TIMEOUT;
DWORD CSocketCtrl::m_dwAcceptOut = ALL_TIMEOUT;
DWORD CSocketCtrl::m_dwReadByte = 0;
DWORD CSocketCtrl::m_dwWriteByte = 0;
// 接收数据
BOOL CSocketCtrl::RecvMsg( char *sBuf )
{
if( !HasData() )
return FALSE;
MsgHeader header;
int nbRead = this->Recv( (char*)&header, sizeof( header ), MSG_PEEK );
if( nbRead == SOCKET_ERROR )
return FALSE;
if( nbRead < sizeof( header ) )
{
this->Recv( (char*)&header, nbRead, 0 );
printf( "\ninvalid msg, skip %ld bytes.", nbRead );
return FALSE;
}
if( this->Recv( (char*)sBuf, header.stLength, 0 ) != header.stLength )
return FALSE;
return TRUE;
}
// 发送数据
int CSocketCtrl::SendMsg( char *sBuf,unsigned short stSize )
{
static char sSendBuf[ 4000 ];
memcpy( sSendBuf,&stSize,sizeof(short) );
memcpy( sSendBuf + sizeof(short),sBuf,stSize );
if( (sizeof(short) + stSize) != this->Send( sSendBuf,stSize+sizeof(short),0 ) )
return -1;
return stSize;
}
// 启动winsock
BOOL CSocketCtrl::StartUp()
{
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD( 1, 1 );
int err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
{
return FALSE;
}
return TRUE;
}
// 关闭winsock
BOOL CSocketCtrl::ShutDown()
{
WSACleanup();
return TRUE;
}
// 得到主机名
BOOL CSocketCtrl::GetHostName( char szHostName[], int nNameLength )
{
if( gethostname( szHostName, nNameLength ) != SOCKET_ERROR )
return TRUE;
return FALSE;
}
BOOL CSocketCtrl::IsIPsChange()
{
return FALSE;
static int iIPNum = 0;
char sHost[300];
hostent *pHost;
if( gethostname(sHost,299) != 0 )
return FALSE;
pHost = gethostbyname(sHost);
int i;
char *psHost;
i = 0;
do
{
psHost = pHost->h_addr_list[i++];
if( psHost == 0 )
break;
}while(1);
if( iIPNum != i )
{
iIPNum = i;
return TRUE;
}
return FALSE;
}
// socket是否可以写
BOOL CSocketCtrl::CanWrite()
{
int e;
fd_set set;
timeval tout;
tout.tv_sec = 0;
tout.tv_usec = 0;
FD_ZERO(&set);
FD_SET(m_sockfd,&set);
e=::select(0,NULL,&set,NULL,&tout);
if(e==SOCKET_ERROR) return FALSE;
if(e>0) return TRUE;
return FALSE;
}
// socket是否有数据
BOOL CSocketCtrl::HasData()
{
int e;
fd_set set;
timeval tout;
tout.tv_sec = 0;
tout.tv_usec = 0;
FD_ZERO(&set);
FD_SET(m_sockfd,&set);
e=::select(0,&set,NULL,NULL,&tout);
if(e==SOCKET_ERROR) return FALSE;
if(e>0) return TRUE;
return FALSE;
}
int CSocketCtrl::Recv( char* pBuffer, int nSize, int nFlag )
{
return recv( m_sockfd, pBuffer, nSize, nFlag );
}
int CSocketCtrl::Send( char* pBuffer, int nSize, int nFlag )
{
return send( m_sockfd, pBuffer, nSize, nFlag );
}
BOOL CSocketCtrl::Create( UINT uPort )
{
m_sockfd=::socket(PF_INET,SOCK_STREAM,0);
if(m_sockfd==INVALID_SOCKET) return FALSE;
SOCKADDR_IN SockAddr;
memset(&SockAddr,0,sizeof(SockAddr));
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = INADDR_ANY;
SockAddr.sin_port = ::htons( uPort );
if(!::bind(m_sockfd,(SOCKADDR*)&SockAddr, sizeof(SockAddr)))
{
SetDefaultOpt();
return TRUE;
}
Close();
return FALSE;
}
void CSocketCtrl::Close()
{
::closesocket( m_sockfd );
m_sockfd = INVALID_SOCKET;
}
BOOL CSocketCtrl::Connect( LPCTSTR lpszHostAddress, UINT nHostPort )
{
if(m_sockfd==INVALID_SOCKET) return FALSE;
SOCKADDR_IN sockAddr;
memset(&sockAddr,0,sizeof(sockAddr));
LPSTR lpszAscii=(LPSTR)lpszHostAddress;
sockAddr.sin_family=AF_INET;
sockAddr.sin_addr.s_addr=inet_addr(lpszAscii);
if(sockAddr.sin_addr.s_addr==INADDR_NONE)
{
HOSTENT * lphost;
lphost = ::gethostbyname(lpszAscii);
if(lphost!=NULL)
sockAddr.sin_addr.s_addr = ((IN_ADDR *)lphost->h_addr)->s_addr;
else return FALSE;
}
sockAddr.sin_port = htons((u_short)nHostPort);
int r=::connect(m_sockfd,(SOCKADDR*)&sockAddr,sizeof(sockAddr));
if(r!=SOCKET_ERROR) return TRUE;
int e;
e=::WSAGetLastError();
if(e!=WSAEWOULDBLOCK) return FALSE;
fd_set set;
timeval tout;
tout.tv_sec = 0;
tout.tv_usec = 100000;
UINT n=0;
while( n< CSocketCtrl::m_dwConnectOut)
{
FD_ZERO(&set);
FD_SET(m_sockfd,&set);
e=::select(0,NULL,&set,NULL, &tout);
if(e==SOCKET_ERROR) return FALSE;
if(e>0) return TRUE;
if( IsIPsChange() )
return FALSE;
n += 100;
}
return FALSE;
}
// 设置监听socket
BOOL CSocketCtrl::Listen( int nBackLog )
{
if( m_sockfd == INVALID_SOCKET ) return FALSE;
if( !listen( m_sockfd, nBackLog) ) return TRUE;
return FALSE;
}
// 接收一个新的客户连接
BOOL CSocketCtrl::Accept( CSocketCtrl& ms )
{
if( m_sockfd == INVALID_SOCKET ) return FALSE;
if( ms.m_sockfd != INVALID_SOCKET ) return FALSE;
int e;
fd_set set;
timeval tout;
tout.tv_sec = 0;
tout.tv_usec = 100000;
UINT n=0;
while(n< CSocketCtrl::m_dwAcceptOut)
{
//if(stop) return FALSE;
FD_ZERO(&set);
FD_SET(m_sockfd,&set);
e=::select(0,&set,NULL,NULL, &tout);
if(e==SOCKET_ERROR) return FALSE;
if(e==1) break;
n += 100;
}
if( n>= CSocketCtrl::m_dwAcceptOut ) return FALSE;
ms.m_sockfd=accept(m_sockfd,NULL,NULL);
if(ms.m_sockfd==INVALID_SOCKET) return FALSE;
ms.SetDefaultOpt();
return TRUE;
}
BOOL CSocketCtrl::Create(void)
{
m_sockfd=::socket(PF_INET,SOCK_STREAM,0);
if(m_sockfd==INVALID_SOCKET) return FALSE;
SOCKADDR_IN SockAddr;
memset(&SockAddr,0,sizeof(SockAddr));
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = INADDR_ANY;
SockAddr.sin_port = ::htons(0);
//if(!::bind(m_sock,(SOCKADDR*)&SockAddr, sizeof(SockAddr)))
{
SetDefaultOpt();
return TRUE;
}
Close();
return FALSE;
}
// 设置正确的socket状态,
// 主要是主要是设置非阻塞异步传输模式
void CSocketCtrl::SetDefaultOpt()
{
struct linger ling;
ling.l_onoff=1;
ling.l_linger=0;
setsockopt( m_sockfd, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling));
setsockopt( m_sockfd, SOL_SOCKET, SO_REUSEADDR, 0, 0);
int bKeepAlive = 1;
setsockopt( m_sockfd, SOL_SOCKET, SO_KEEPALIVE, (char*)&bKeepAlive, sizeof(int));
BOOL bNoDelay = TRUE;
setsockopt( m_sockfd, IPPROTO_TCP, TCP_NODELAY, (char*)&bNoDelay, sizeof(BOOL));
unsigned long nonblock=1;
::ioctlsocket(m_sockfd,FIONBIO,&nonblock);
}