posts - 495,comments - 227,trackbacks - 0
根据前面一节的说明,服务端套接字应该按照如下顺序建立:
  1. 初始化
  2. 创建套接字
  3. 绑定本地地址
  4. 进入侦听状态
  5. 处理接受循环

下面首先创建一个例子来演示服务端套接字的实现,并在以后的各节中优化这个设计。

这个设计实现的功能如下:允许客户端(实际上就是telnet程序)登陆,并对客户端的输入回显。

4.1 准备工程

为了实现这个demo,我打算使用Visual Studio 6.0提供的对话框模板来实现,请按照下述步骤准备工程:

  1. 启动Visual C++ 6.0
  2. 选择File/New/Project
  3. 选择MFC AppWizard(exe)
  4. 在Project name中输入demo1, Next
  5. 选择Dialog Base, Next
  6. 在Windows Sockets前打钩
  7. 其他都保持缺省值,点击Next完成向导
  8. 在工程中加入两个文件:sockutil.h和sockutil.cpp:这两个文件将保存公共的函数,避免以后重复编写。

这样创建的工程会自动加入WinSock2支持,具体的内容包括:

  1. 在stdafx.h中加入#include <afxsock.h>,该头文件包含如下语句载入对应LIB:
    #pragma comment(lib, "wsock32.lib")
  2. 在InitInstance中加入如下代码:
    if(!AfxSocketInit())
    {
    AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
    return FALSE;
    }
  3. 在资源中定义字符串资源IDP_SOCKETS_INIT_FAILED:Windows通讯端口初始化失败

如果大家创建工程时没有加入对应的sock支持,我们可以参照上述列表手工加入。

对于前文所述的sockutil.h文件,加入如下初始代码:

#if !defined(__SOCKET_UTILITY_HEADER__)
#define __SOCKET_UTILITY_HEADER__
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#endif

对于sockutil.cpp文件,则加入如下初始代码:

#include "stdafx.h"
#include "sockutil.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

4.2 准备错误处理方式

在编制每个程序之前,我习惯是为错误处理定制一个统一的处理方式。在以后的例子中,我会按照如下的方式处理错误:在一个log文件中记录错误发生的位置(文件、行号),错误号和字符串解释。为了实现这个目的,我在sockutil.cpp定义如下的函数:

bool ErrorHandle(LPCTSTR expression, bool bFalse, LPCTSTR file, UINT line)
{
if(!bFalse)
{//没有错误,直接返回
return false;
}
FILE * fp;
fp = fopen("demo.log","at");
if(NULL == fp)
{//如果文件打开失败,放弃记录
return true;
}
//获得错误码
DWORD ErrorCode = GetLastError();
//格式化成字符串格式
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
ErrorCode ,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);
//输出到文件
fprintf(fp,_T("file=%s,line=%u"n%d:%s"n"),file,line,ErrorCode, (LPCTSTR)lpMsgBuf);
// 释放空间,该空间由FormatMessage分配
LocalFree( lpMsgBuf );
//关闭文件
fclose(fp);

return true;
}

在sockutil.h中,添加如下代码:

bool ErrorHandle(LPCTSTR expression, bool bFalse, LPCTSTR file, UINT line);
#define ERRORHANDLE(expression) ErrorHandle(#expression,(expression),__FILE__,__LINE__)

4.3 核心代码

根据前文,设计该服务端套接字主函数如下:

void sockmain(LPCTSTR ip, UINT port)
{

SOCKET hSocket;
hSocket = socket(AF_INET,SOCK_STREAM,0);
if(ERRORHANDLE(hSocket == INVALID_SOCKET))
{
return;
}
sockaddr_in addr;
InitializeAddress(inet_addr(ip), port, addr);
if(ERRORHANDLE(SOCKET_ERROR == bind(hSocket, (const sockaddr*) & addr, sizeof(addr))))
{
closesocket(hSocket);
return;
}
if(ERRORHANDLE(SOCKET_ERROR == listen(hSocket,5)))
{
closesocket(hSocket);
return;
}
SOCKET hClient;
int size;
char buffer[2048];
int length;
size = sizeof(addr);
while(INVALID_SOCKET != (hClient = accept(hSocket,(sockaddr*)&addr, & size)))
{
size = sizeof(addr);
while((length = recv(hClient, buffer, sizeof(buffer),0)) > 0)
{
SendData(hClient,buffer, length);
}
closesocket(hClient);
}
closesocket(hSocket);

return;
}

其中,InitializeAddress定义如下:

void InitializeAddress(DWORD ip, UINT port, sockaddr_in & addr)
{
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr= ip;
addr.sin_port = htons(port);
}
SendData定义如下:
int SendData(SOCKET hSocket, const char * data, int length)
{
int result;
int pos = 0;
while(pos < length)
{
result = send(hSocket, data + pos, length - pos , 0);
if(result > 0 )
{
pos += result;
}else{
return result;
}
}
return length;
}

4.4 启动该任务

为了启动服务端套接字,我们可以在对话框资源的OK按钮上双击,然后在OnOK中添加如下代码

   sockmain("0.0.0.0",2000);

当我们启动该工程,并点击OK按钮,就可以通过telnet来测试是否有回显功能了。

posted on 2008-07-03 15:19 SIMONE 阅读(630) 评论(0)  编辑  收藏 所属分类: C++

只有注册用户登录后才能发表评论。


网站导航: