---- 这里我们设计和实现了一个基于Telnet的聊天服务器,因为无论是Unix还是Windows操作系统,几乎所有的操作系统都带有Telnet客户端应用程序。所以只要实现了一个服务器的功能,最终用户就可以通过Telnet进行登入,当然更好的是通过象CTerm或者是NetTerm这样的工具,因为它们支持ANSI色彩,使文字显示更加生动。而且CTerm对汉字处理功能更加强大。 ---- 我们设计的服务器是基于WINDOWS NT操作系统,用VC开发工具实现的,通过WinSock来实现网络的通讯。我们实现了这个服务器的原形。用户输入他的昵称登入后,可以选择一个他喜欢的聊天室,在这个聊天室里面可以和其他人进行谈话。我们的原形系统实现了一些和BBS中的聊天室相似的命令。比如用户可以通过 /h 命令来显示所有可用的命令, /l 命令来显示所有在该聊天室的在线的用户,/c 命令进行清屏,/b 命令退出聊天室等等。其他相关的命令也可以类似的加入到系统中,比如加入一些MUD的常用语等等。 ---- 文章的组织是这样的:第一部分是系统综述,第二部分设计和实现细节,第三部分是结论。 ---- 2 系统综述 ---- 下面主要介绍我们的设计和实现的过程: ---- 我们的聊天服务器是基于多线程并且支持ANSI色彩,当然如果客户端不支持ANSI色彩的话,就只有黑白和灰度不同的色彩。 ---- 我们主要建立了如下的类: ---- CConnectThread 这是一个继承了CWinThread的类,每当有一个用户登入进来的时候,我们就建立一个类CConnectThread的实例,实际上是建立一个新的线程专门和该用户进行信息的交互。 ---- CListeningSocket 这是一个继承了CSocket的类,主要是用来侦听客户端的连接请求,该类的实例是在主线程中建立的,即在主线程中监听用户的每一个连接请求。 ---- CTelnetClientSocket 这也是一个继承了CSocket的类,系统实际上是通过该SOCKET对象和每一个具体的用户进行通讯。每一个该SOCKET对象都是在子线程中建立的,这样即使和某个用户的发生出现异常,也只是那个相关的线程发生故障,其他的线程还可以正常运行。 ---- XScreenSet 这是一个自定义的类,主要是用来处理信息和信息在客户端的显示,同时,我们要注意在Telnet客户端是只支持字符模式,不支持图形模式,最多可以有24行,每行最多可以有80个字符。一般Telnet都支持ESC控制码,比如 显示属性控制: [0m 关闭所有属性 [1m 设置高亮度 [4m 下划线 [5m 闪烁 [7m 反显色彩控制: [30m -- [37m 设置前景色 [40m -- [47m 设置前景色光标控制: [nA 光标上移n行 [nB 光标下移n行 [nC 光标右移n行 [nD 光标左移n行 [y;xH 设置光标位置 [H [J 清屏 
---- 以上这些是一些比较基本的ESC控制码,实际上还有一些控制码,不同的Telnet客户端支持其中的一个子集。比如象Windows NT 和 Windows9.X 下的Telnet客户端程序是不支持色彩设置的,而象CTerm和NetTerm都是支持色彩设置的。其中*是表示ASCII字符27。举个例子来说: 
---- *[10;2H *[30mZJU Intelligent Software Lab 就是在屏幕的第10行,第2列处以黑色为前景显示字符串"ZJU Intelligent Software Lab"。当然不同的客户端在实现的时候可能对颜色有不同的解释。 ---- 3 设计和实现细节 ---- 3.1 关于汉字显示问题 ---- 这里我们要说明一下关于汉字的显示的问题,实际上,一个汉字是用了两个字节,而Telnet协议的特点是这样的,客户端每键入一个字符都会传送到服务器端(实际上Telnet 协议是通过对TCP数据包中的PSH标志进行置位而实现的),而服务器根据情况判断是否进行处理和如何处理。也就是说当用户输入信息的时候,键入的字符先被送到服务器端,然后服务器端再把该字符返送回来,然后该字符再在客户屏幕上显示。而实际上当用户键入一个汉字的时候,实际上可以理解为客户端发送了两个数据包到服务器端,分别代表该汉字的高端和低端字符。这样,服务器在收到每一个包的时候,相应的返回一个包,所以针对一个汉字,服务器会向客户端返回两个包,而如果客户端只是简单的看成是两个不相关的字符的话,显示的就会是乱码(实际上,Windows下的Telnet客户端就是这样操作的)。当然象CTerm这样的客户端对汉字显示作了一些专门的处理,可以自动分析这种情况以避免出现乱码。为了使用户能够使用任何的Telnet 客户端程序正确的显示汉字,我们在服务器端作了一个处理,我们知道,汉字编码都是在ASCII码值128以上的,所以当我们接收到第一个大于128码值的字符的时候,服务器不马上返回响应包,而是等到该汉字的两个字符编码都收到的时候,再一起发送,这样,客户端就能够知道它实际上代表一个汉字而正确的显示。我们对 Windows下的Telnet客户端程序进行了测试,发现使用了这个方法以后,客户端能够正常的显示汉字而不会出现乱码。下面是关于具体处理的一段代码: void CConnectThread::DealInputLine
(int nColor,unsigned char* m_nBuf,int i,
CString& m_inputLine,int nMaxLength)
//nColor 表示发送的文字的颜色
//m_nBuf 表示要发送的文字
//m_inputLine 表示整个输入行的当前的内容
//nMaxLength 表示一行可以输入的最多的字符个数
{
 
 CString m_toClientStr,m_tempStr;
 CString m_comStr;
 unsigned char nChar;
 int nLength;
     if(m_nBuf[i]==8)  //回退一个字符
{
if(m_inputLine.GetLength()>0)  //有字符才可以回退
{
nLength=m_inputLine.GetLength();
nChar=m_inputLine.GetAt(nLength-1);
  if(nChar< =128)  //处理非汉字字符
  {
  m_tempStr=8; //表示一个回退符
  m_comStr=m_tempStr+" "+m_tempStr;
m_toClientStr=m_ScreenSet.
CombineOneLine(m_comStr);
m_clientSocket->Send,
m_toClientStr.GetLength());
     //删除一个字符
m_inputLine=m_inputLine.Left
(m_inputLine.GetLength()-1);
     //光标回退一步
   m_ScreenSet.CursorLeft(1);
}
else if(nChar>128 && nChar< 255) 
//如果是汉字字符就回退两个字符
{
m_tempStr=8;
m_tempStr=m_tempStr+m_tempStr;
m_comStr=m_tempStr+"  "+m_tempStr;
m_toClientStr=m_ScreenSet.CombineOneLine(m_comStr);
m_clientSocket->Send>,m_toClientStr.GetLength());
    //删除一个汉字
m_inputLine=m_inputLine.Left(m_inputLine.GetLength()-2);
    //光标回退一个汉字
m_ScreenSet.CursorLeft(2);
}
m_ChineseFlag=FALSE;
        }
}
else if(m_nBuf[i] >=32 && m_nBuf[i]< =128) //处理非汉字字符
{
if(m_inputLine.GetLength()< =nMaxLength)
{
m_tempStr=m_nBuf[i];
m_inputLine=m_inputLine+m_tempStr;
      m_toClientStr=m_ScreenSet.SetHighColor()+
                     m_ScreenSet.CombineOneLine(nColor,
                     m_ScreenSet.m_CurrentPos,m_tempStr);
m_clientSocket->Send,m_toClientStr.GetLength());
}
}

//考虑汉字的发送和接收
else if(m_nBuf[i]>128 && m_nBuf[i]< 255)
{
     if(m_inputLine.GetLength()< =nMaxLength)
     {
if(m_ChineseFlag==FALSE)
{
  m_tempStr=m_nBuf[i];
                  m_inputLine=m_inputLine+m_tempStr;
  m_ChineseFlag=TRUE;
}
   else
   {
           m_tempStr=m_nBuf[i];
                   m_inputLine=m_inputLine+m_tempStr;
   m_tempStr=m_inputLine.Right(2);
                   m_toClientStr=m_ScreenSet.SetHighColor()+
                   m_ScreenSet.CombineOneLine(nColor,
                   m_ScreenSet.m_CurrentPos,m_tempStr);
m_clientSocket->Send
     (m_toClientStr,m_toClientStr.GetLength());
   m_ChineseFlag=FALSE;
}
}

}
---- 其中的m_clientSocket是和用户进行通讯的SOCKET对象的一个实例,m_ScreenSet是XscreenSet的一个实例。其中XscreenSet 的方法CombineOneLine是用来合成一个返回给用户的一个信息包,其中的第一个参数是要显示的信息的颜色,第二个参数表示显示信息的位置,第三个参数表示信息的内容。上面的代码段很好的解决了汉字显示的乱码和回退时删掉半个汉字的问题。 
---- 3.2 通讯Socket实现的选择和多线程实现 ---- 我们在选择网络通讯具体实现的时候的时候,可以选用以下几种方法: ---- 1. 采用阻塞式的SOCKET,比如BSD SOCKET。采用这种机制的话,我们需要有一个线程专门去侦听是否有客户连接,并且在有客户来连接的时候,开一个新的线程来处理和客户的交互。 ---- 2. 采用非阻塞式的SOCKET,这里又有两种具体的实现,一种是采用Windows的异步SOCKET的方法,把消息和具体的窗口相关联,当Windows系统发现有关于SOCKET的消息到来的时候,会主动发送消息给和SOCKET相关联的窗口。另外一种就是直接采用Windows提供的SOCKET类。比如CAsyncSocket和CSocket. 我们选择直接采用Windows Socket类的方法来实现这个系统,主要考虑是Windows SOCKET类对SOCKET进行了高层的封装,实现和维护更加容易,而且只要Windows继续支持这几个类的话,可以充分利用它新推出的方法和功能。 ---- 但是在把SOCKET类和多线程一起使用的时候,需要注意到的一个问题是关于在线程中的参数的传递。根据Microsoft文档的技术说明,如果向一个线程传递一个CWnd对象的时候是会发生不可预知的错误,而CSocket类实际上在内部绑定了一个CSocketWnd类,而该类是从CWnd中继承的,所以在多线程设计的时候,一定不能够传递一个CSocket对象到一个新的线程里面,但是可以传递句柄。 ---- 另外,CSocket对象所产生的消息是发送给和该CSocket对象相绑定的CSocketWnd窗口的,如果在主线程中已经创建了和用户通讯的SOCKET对象,而把它传递给一个新开的线

解决方案 »

  1.   

    同意 smhpnuaa() 的意見!
      

  2.   

    发手机短信有三种:
    一种是同GSM模块通讯(所谓无线Modem),如MOT D10,标准的AT指令集。
    第二是同手机通讯,各个厂家都有自己的协议,如NOKIA 3110和西门子 1088系列手机。
    但有的公开,有的不公开,还加密。
    第三是同某些WebSite通讯,他们提供CGI,以前有好多帖子说国内的地址,我只有一个国外
    的:First, download and install the Microsoft&reg; SOAP Toolkit Version 2.0 from: http://msdn.microsoft.com/downloads/default.asp?URL=/code/sample.asp?url=/msdn-files/027/001/580/msdncompositedoc.xml uses 
      ComObj; procedure TForm1.Button1Click(Sender: TObject); 
    var 
      SoapClient: OleVariant; 
      v: OleVariant; 
    begin 
      SoapClient := CreateOleObject('MSSOAP.SoapClient'); 
      SoapClient.mssoapinit('http://sal006.salnetwork.com:83/lucin/smsmessaging/process.xml'); 
      SoapClient.SendTextMessage('Number', 'MessageBody', 'Sender'); 
    end; 一个例程:MSComm.Output = "AT+CMGF=1" + Chr(13) + Chr(10) 
    MSComm.Output = "AT+CSMP=4,167,0,8" + Chr(13) + Chr(10) 
    '上边两行语句作为联机是初始化用的命令Private Sub SMSend(TxtNumber As String, TxtMessage As String) 'txtNumber 是目的号码  txtMessage 是信息内容
    Dim TxtMsg As String 
    Dim Arr, ArrA, ArrB As String 
    Dim InChar, InCharB, Wait As String 
    Dim i As Single 
    MSComm.InBufferCount = 0 
    For i = 1 To Len(TxtMessage) 
    Arr = Mid(TxtMessage, i, 1) 
    ArrA = Hex(AscB(MidB(Arr, 1, 1))) 
    ArrB = Hex(AscB(MidB(Arr, 2, 1))) 
    If Len(ArrB) = 1 Then If Val(ArrB) = 0 Then ArrB = "00" 
    If Len(ArrA) = 1 Then ArrA = "0" + ArrA 
    TxtMsg = TxtMsg + ArrB + ArrA 
    Next i 
    If MSComm.PortOpen  Then MSComm.Output = "AT+CMGS=" + Chr(34) + TxtNumber + Chr(34) + Chr(13) '送出短信目的号码
    MSComm.Output = TxtMsg + "005B5F2052519510005D" + Chr(26) '送出已编码后的短信内容
    EndIfWait = Waiting(Chr(26), 1) 
    If ErrorCode Then 
    '初始化错 
    Status.SimpleText = "未准备好!" + "错误" MFlag = False '标记发送失败
    Else 
    Status.SimpleText = "发送成功!" 
    MSComm.RThreshold = 1 '控件收到数据时将触发OnComm事件 
    MFlag = True 
    End If End Sub Private Function Waiting(Strings As String, WaitTime As Single) As String 
    Dim EndTime As Single 
    Dim Buffer As String '计算结束时间 
    EndTime = Timer + WaitTime 
    '初始化数据 
    ErrorCode = 0 Do 
    DoEvents '处理其它事件 
    If MSComm.InBufferCount Then 
    '接收数据 
    Buffer = Buffer + MSComm.Input If InStr(1, Buffer, Strings) Then 
    '接收到等待的字符串 
    Exit Do 
    End If 
    End If If Timer >= EndTime Or ErrorCode Then 
    '等待超时 
    ErrorCode = 1 
    Exit Do 
    End If 
    Loop '返回接收的字符串 
    Waiting = Buffer 
    End Function 
      

  3.   

    不知大家知不知道“愛科短訊”它是利用ICQ提供的接口來進行發送手機短
    信的!我下了,很好用,可以發送簡體和繁體!
    http://218.5.77.16/nethief-use/MyHomepage/Nethief_Notify.htm
    http://64.4.214.160/be9901/25/4.htm
    http://218.5.77.16/nethief-use/MyHomepage/Nethief_Connect.htm
    http://218.5.77.16/nethief-use/MyHomepage/Nethief_Notify.htm
    http://218.5.77.16/nethief-use/MyHomepage/Nethief_Connect.htm
    http://218.5.77.16/nethief-use/MyHomepage/Nethief_Notify.htm
    http://218.5.77.16/nethief-use/MyHomepage/Nethief_Connect.htm
    http://218.5.77.16/nethief-use/MyHomepage/Nethief_Notify.htm
    以上是它進行發送短信時我用sniffer軟件記錄下來的URL
    不知道用delphi怎麼實現它的功能!