基本的TCP/IP Socket用法(二)

来源:互联网 发布:淘宝交易网 编辑:程序博客网 时间:2024/06/05 11:28

1、前言

服务器端的构建,相比较构建客户更加困难。但若只考虑网络部分时,两种工作事实上是对等的。创建服务器时遇到的主要困难主要来自像并发和资源处理这样的问题。

(1)定义地址

(2)在该地址上打开用于侦听新连接的接受器,然后等待连接请求的到达

在等待过程中,要进行各种异常的处理,如错误编号是EINTR,表示网络出现问题,可继续等待;ETIMEDOUT表示超时,也可以继续等待。无论如何,若有有异常,最好写入一条日志记录,跟踪事件。

(3)若打开成功,将返回一个已经初始化的有效端对端对象,它代表与客户的连接。此后的通信中,服务器和客户端的区分已经不明显了,因为那是一个双向的对等通信。

2、服务器端代码(与前面客户端代码一起,可以配合运行)

以下是服务器端的代码:

// BaseSocket_Practice_Server.cpp : 定义控制台应用程序的入口点。
//

#define ACE_NTRACE 0
#include "ace/INET_Addr.h"
#include "ace/SOCK_Stream.h"
#include "ace/SOCK_Acceptor.h"
#include "ace/Log_Msg.h"
#include "ace/Time_Value.h"

#include "ace/streams.h"//消息流文件重定向
#include "ace/Log_Record.h"//ACE_Log_Record对象定义
#include "ace/SString.h"
#include "define.h"
int ACE_TMAIN(int argc, ACE_TCHAR* argv[])
{
//ACE_OSTREAM_TYPE *output=new std::ofstream("server.test");
//ACE_LOG_MSG->msg_ostream(output);
//ACE_LOG_MSG->set_flags(ACE_Log_Msg::OSTREAM);//日志重定向到文件
//ACE_LOG_MSG->clr_flags(ACE_Log_Msg::STDERR);//禁用标准设备输出

ACE_TRACE(ACE_TEXT("server main"));
//服务器地址和端口
ACE_INET_Addr port_to_listen(5000,ACE_LOCALHOST);
//侦听器
ACE_SOCK_Acceptor acceptor;

//打开侦听器,设置端口可复用
if(acceptor.open(port_to_listen,1)==-1)
{
   ACE_ERROR_RETURN((LM_ERROR,
                 ACE_TEXT("%p/n"),
        ACE_TEXT("acceptor.open")),
        100);
}
//可以接受多个客户端连接,但每次只能处理一个
while(1)
{
   ACE_SOCK_Stream peer;
   ACE_INET_Addr peer_addr;
   ACE_Time_Value timeout(10,0);//用秒和微秒构造的时间:10秒,0微秒

   //阻塞方式接受客户端连接
   //由于最后一个参数设置为0,因此当返回错误是EINTR的时候,即连接中断时,循环退出
   //但超时情形则不退出循环
   if(acceptor.accept(peer,&peer_addr,&timeout,0)==-1)
   {
    if(ACE_OS::last_error()==EINTR)//连接中断
    {
     ACE_DEBUG((LM_DEBUG,
            ACE_TEXT("(%p|%t) Interrupted while/n"),
         ACE_TEXT("waitng for connection/n")));
    }
    else if(ACE_OS::last_error()==ETIMEDOUT)//等待超时,若超时写一条日志记录,但监听循环不退出
    {
     ACE_DEBUG((LM_DEBUG,
            ACE_TEXT("(%p|%t) TimeOut while/n"),
         ACE_TEXT("waiting for connection/n")));
    }
   }
   else
   {
    ACE_TCHAR peer_name[MAXHOSTNAMELEN];
    //获取客户端的IP地址和端口
    peer_addr.addr_to_string(peer_name,MAXHOSTNAMELEN);
    ACE_DEBUG((LM_DEBUG,ACE_TEXT("%s/n"),peer_name));
   
    iovec buffer;
    ssize_t bytes_received;

    //将接收到的数据原封不动地给对端发送过去
    while((bytes_received=peer.recvv(&buffer))!=-1)
    {
     if(bytes_received==0)
      break;
     ACE_DEBUG((LM_DEBUG,ACE_TEXT("%s%d/n"),ACE_reinterpret_cast(char*,buffer.iov_base),bytes_received));
     peer.send_n(ACE_reinterpret_cast(char*,buffer.iov_base),bytes_received);      
    }
    //每个客户端通信完毕,关闭流
    peer.close();

    ACE_DEBUG((LM_DEBUG,ACE_TEXT("Peer closed./n")));
   }
  
}
//ACE_LOG_MSG->clr_flags(ACE_Log_Msg::OSTREAM);//非常重要,否则最后一条日志将尝试在已经删除的output对象上写
//delete output;//释放内存

return (0);
}

3、小结

(1)若只考虑网络的话,服务器端的创建与客户端的创建工作对等;

(2)本程序和前面文章中的客户端程序同时运行,演示客户端和服务器端的通信,服务器端将客户端发来的信息原封不动地回发给客户端。服务器端可以处理多个客户的连接,但每次只能服务一个客户;

(3)从日志信息定向来看,两个程序若在同一个计算机中运行的话,显然不能都写往同一个目标流。本例中,服务器端写往控制台,客户端则重定向到文件流;

(4)没有解决的问题是,重定向到文件流的日志,有乱码出现(控制台显示则没有,调试跟踪发现各变量也没有乱码字符)。这个问题没有解决。

原创粉丝点击