用C++ Builder中的TServerSocket,TClientSocket來寫網絡通訊程序

本文主要介紹如何在C++ Builder中用TServerSocket,TClientSocket來寫一個網絡間短包,文件傳輸的程序,這個程序可以支持:1.局域網上的傳輸。2.局域網與公網的傳輸(雙向傳輸),在第二篇文章中我將用socket api寫一個客戶端和服務器,功能和本文中的功能一樣。使用通訊協議TCP,這裏的客戶端和服務器使用的都是阻塞模式---多線程。
Client:
.h File
class ClientThread : public TThread
{
private:
    AnsiString File;
    TClientSocket* ClientSocket;
    TWinSocketStream* WskStream;
protected:
    void __fastcall Execute();
public:
    __fastcall ClientThread(AnsiString IPAddr,
        WORD Port, AnsiString file);

};

.cpp File
void __fastcall ClientThread::Execute()
{

    //Send Text or SendFile
    UINT TimeOut=60000;
    char buf[4096];
    //char IPAddress[32];
    //GetIPAddress(IPAddress);//IPAddress
    WskStream = new TWinSocketStream(ClientSocket->Socket, TimeOut);
    if(Form1->CheckBox1->Checked)//Determine whether to send short package or send file.
    {
      String S=Form1->TxtEdit->Text;
      int TxtLen=Form1->TxtEdit->Text.Length();
      strncpy(buf,S.c_str(),TxtLen);
      ClientSocket->Active=true;
      WskStream->Write("TEXT/0",5);//Send Text Flag
      WskStream->Write(IPAddress,32);//Send IP Address
      WskStream->Write(buf,TxtLen);//Send Text String
      WskStream->Write(buf,TxtLen);
      if(WskStream->WaitForData(TimeOut))
      {
         buf[0]='/0';
         FlagBuf[0]='/0';
         IPAddress[0]='/0';
         WskStream->Read(FlagBuf,5);
         WskStream->Read(IPAddress,32);
         int nSize=0;
         nSize=WskStream->Read(buf,TxtLen);
         buf[nSize]='/0';
         if(!StrPas(buf).IsEmpty())
         {
            SaveLog("Received a text!");
            SaveLog("Client:"+StrPas(IPAddress)+"/r/nStart Time:"+DateTimeToStr(Now()));
            SaveLog("Text Content:"+StrPas(buf));
            FLASHWINFO FSHINFO;
            ::ZeroMemory(&FSHINFO,sizeof(FLASHWINFO));
            FSHINFO.cbSize=sizeof(FLASHWINFO);
            FSHINFO.hwnd=Application->Handle;
            FSHINFO.dwFlags=FLASHW_TRAY|FLASHW_CAPTION;
            FSHINFO.uCount=10;
            FSHINFO.dwTimeout=200;
            ::FlashWindowEx(&FSHINFO);
            Form1->RecEdit->Lines->Add("Received Length:"+String(nSize));
            Form1->RecEdit->Lines->Add("Received:"+StrPas(buf));

            //SaveLog("Client:"+StrPas(IPAddress)+"/r/nEnd Time:"+DateTimeToStr(Now()));
         }
      }
    }
    else
    {
      int  nLen;
      int  hFile;
      int  nSize;
      char Path[255];//Path Buffer
      char FileName[255];//FileName Buffer;
      char FileExt[5];//Extension Buffer
      char FlagBuf[5];
      static int num=0;
      AnsiString sFileName=ExtractFileName(File);
      for(int k=sFileName.Length();k>0;k--)
      {
         if(sFileName.SubString(k,1)==".")
         {
            sFileName=sFileName.SubString(1,k-1);
            break;
         }
      }
      AnsiString sPath=ExtractFilePath(File);
      AnsiString sExtension=ExtractFileExt(File);

      strcpy(FileName,sFileName.c_str());//FileName
      strcpy(Path,sPath.c_str());                 //Path
      strcpy(FileExt,sExtension.c_str());    ://Extension
      try {
         hFile = -1;
         ClientSocket->Active = true;
            hFile = FileOpen(File, fmOpenRead);
            if (hFile != -1) {
               nSize = GetFileSize((HANDLE)
                hFile, NULL);
               //Send the Flag
               WskStream->Write("FILE/0",5);
               //Send the name of directory
               WskStream->Write(Path,255);
               //Send the filename
               WskStream->Write(FileName,255);
               //Send  the extension of file
               WskStream->Write(FileExt,5);
               //Send client's IP addresss
               WskStream->Write(IPAddress,32);

               //Send the length
               WskStream->Write(&nSize, 4);
               //Send the data
               for(; nSize>0; nSize-=nLen) {
                  nLen = min((int)sizeof(
                    buf), nSize);
                  nLen = FileRead(hFile, buf,
                    nLen);
                  if (nLen<=0) break;
                  WskStream->Write(buf, nLen);
               }
         }
         FileClose(hFile);//Send Completely

         //Client is beginning to read data

         if(WskStream->WaitForData(TimeOut))
         {
            WskStream->Read(FlagBuf,5);
         }
         if(WskStream->WaitForData(TimeOut))//Obtain the directory's name
         {
           WskStream->Read(Path,255);
         }
         //If the directory obtained from  client doesnot exist,the create it
         if(!DirectoryExists(StrPas(Path)))
         {
            CreateDir(StrPas(Path));
         }
         //Obtain the FileName
         if(WskStream->WaitForData(TimeOut))
         {
            WskStream->Read(FileName,255);
         }
         //Obtain the extension
         if(WskStream->WaitForData(TimeOut))
         {
            WskStream->Read(FileExt,5);
         }
         //Obtain the client's IPAddress
         if(WskStream->WaitForData(TimeOut))
         {
            WskStream->Read(IPAddress,32);
         }
         AnsiString S=StrPas(Path)+StrPas(FileName)+StrPas(FileExt);
         buf[0]='/0';
         strcpy(buf,S.c_str());
         while(1) {
            if (FileExists(buf)) {
                  S=StrPas(Path)+StrPas(FileName)+"%03d"+StrPas(FileExt);
                  wsprintf(buf, S.c_str(),
                  num);
                  num++;
                  //Created Susscessfully
            } else break;
         }
         hFile = FileCreate(buf);
         if (hFile==-1)
         {
            Application->MessageBox("Failed to create file!","Error",MB_OK+MB_ICONERROR);
            ClientSocket->Active=false;
            delete WskStream;
            Terminate();
            return;
         }
         SaveLog("Received a file:"+StrPas(buf));
         SaveLog("Client:"+StrPas(IPAddress)+"/r/nStart Time:"+DateTimeToStr(Now()));
         try {
            DWORD dwTick = GetTickCount();
            //Obtain the length
            if (WskStream->WaitForData(
               TimeOut)) {
                  nLen = WskStream->Read(
                  &nSize, 4);
               if (nLen!=4) nSize = 0;
            }
            else
                  nSize = 0;
            //Reading data
            for(; nSize>0 && !Terminated;
                  nSize-=nLen) {
               if (!WskStream->WaitForData(
                   5000)) {
                      if (GetTickCount()-dwTick
                     <TimeOut)
                     continue;
                     ::MessageBox(NULL,"Timeout,the thread has been terminated!","Error",MB_OK|MB_ICONERROR);
                     ClientSocket->Active=false;
                     break;
                  }
                  nLen = WskStream->Read(buf,
                     sizeof(buf));
                  dwTick = GetTickCount();
                  if (nLen <= 0) {
                   //Read Error
                     ClientSocket->Active=false;
                     ::MessageBox(NULL,"Read Error,the thread has been terminated!","Error",MB_OK|MB_ICONERROR);
                     break;
                  }
                  //Combine the data
                  FileWrite(hFile, buf, nLen);
            }
            Form1->RecEdit->Lines->Add("You got a file from server,which was saved in "+StrPas(Path));
            SaveLog("Client:"+StrPas(IPAddress)+"/r/nEnd Time:"+DateTimeToStr(Now()));
            FLASHWINFO FSHINFO;
            ::ZeroMemory(&FSHINFO,sizeof(FLASHWINFO));
            FSHINFO.cbSize=sizeof(FLASHWINFO);
            FSHINFO.hwnd=Application->Handle;
            FSHINFO.dwFlags=FLASHW_TRAY|FLASHW_CAPTION;
            FSHINFO.uCount=10;
            FSHINFO.dwTimeout=200;
            ::FlashWindowEx(&FSHINFO);
             FileClose(hFile);
         }
         catch(Exception& e) {
            ClientSocket->Active=false;
            MessageBox(0, e.Message.c_str(),
                  "Error", MB_ICONERROR);
         }

      }
      catch(Exception& e) {
         ClientSocket->Active=false;
         ::MessageBox(0, e.Message.c_str(),
            "Error", MB_OK|MB_ICONERROR);
      }
      FileClose(hFile);
    }
    delete WskStream; // delete ClientSocket;
}

//Begin to send package
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    int Port;
    AnsiString Addr;

    Addr = IPAddr->Text.Trim();
    if (Addr.IsEmpty()) {
        IPAddr->SetFocus();
        Application->MessageBox("Please enter the client's address!","Warning",MB_OK|MB_ICONWARNING);
        return;
    }
    try {
        Port = ClientPort->Text.ToInt();
    }
    catch(Exception& e) {
        ShowMessage(e.Message);
        ClientPort->SetFocus(); return;
    }
    if(CheckBox1->Checked)  //Send Text
    {
      if(TxtEdit->Text.IsEmpty())
      {
         ::MessageBox(0,"Please enter the text string which you want to send!","Error",MB_OK+MB_ICONERROR);
         return;
      }

      new ClientThread(Addr,Port,"");
    }
    else  //Send File
    {
      if (OpenDialog1->Execute())
           new ClientThread(Addr, Port,
               OpenDialog1->FileName); //Begin to send data
    }
}
Server:
.h File
//My Comments:
//At design-time,please place a TServerSocket Component on your form and set its clientype to stThreadBlocking.
//Server Thread Class
//The Server Can not only receives the packages coming from Clients,but also deliver the package to the clients after

processing upon the package.
class SrvThread : public TServerClientThread
{
private:
    UINT FTimeOut;
    TWinSocketStream* WskStream;
    TThread *pThread;
protected:
    void __fastcall ClientExecute();
public:
    __fastcall SrvThread(TServerClientWinSocket*);
    __property UINT TimeOut = { read=FTimeOut, write=FTimeOut };
};
void __fastcall SrvThread::ClientExecute()
{
    TimeOut = 60000; file://60 Seconds
    WskStream = new TWinSocketStream(ClientSocket, TimeOut);
    file://Send Text or File
    char FlagBuf[5];
    char buf[4096];
    char IPAddress[32];
    SMS[0]='/0';
    RecIPAddr[0]='/0';
    if(WskStream->WaitForData(TimeOut)) file://Obtain Flag:File or Text
    {
      WskStream->Read(FlagBuf,5);
    }
    file://Save the flag received from clients
    strcpy(Flag,FlagBuf);
    if(StrPas(FlagBuf)=="TEXT")
    {

      if(WskStream->WaitForData(TimeOut))
      {
         WskStream->Read(IPAddress,32);
      }
      file://Save the client's IPAddress
      strcpy(RecIPAddr,IPAddress);

      if(WskStream->WaitForData(TimeOut))
      {
         WskStream->Read(buf,4096);
      }
      file://Save the short message received from clients
      strcpy(SMS,buf);

      SaveLog("Received a text!");
      SaveLog("Client:"+StrPas(IPAddress)+"/r/nStart Time:"+DateTimeToStr(Now()));
      SaveLog("Text Content:"+StrPas(SMS));
      SaveLog("Client:"+StrPas(IPAddress)+"/r/nEnd Time:"+DateTimeToStr(Now()));
      Form1->Memo1->Lines->Add("Text Content:"+StrPas(buf));
      FLASHWINFO FSHINFO;
      ::ZeroMemory(&FSHINFO,sizeof(FLASHWINFO));
      FSHINFO.cbSize=sizeof(FLASHWINFO);
      FSHINFO.hwnd=Application->Handle;
      FSHINFO.dwFlags=FLASHW_TRAY|FLASHW_CAPTION;
      FSHINFO.uCount=10;
      FSHINFO.dwTimeout=200;
      ::FlashWindowEx(&FSHINFO);
      if(Form1->adv->Checked)//Automatically Deliver To Client
      {
         if(StrPas(Flag).IsEmpty() || StrPas(RecIPAddr).IsEmpty() || StrPas(SMS).IsEmpty())
         {
            Application->MessageBox("You can't deliver because no data are received!","Error",MB_OK+MB_ICONERROR);
            return;
         }
         WskStream->Write(FlagBuf,5);
         WskStream->Write(Form1->ComboBox1->Text.c_str(),32);
         WskStream->Write(SMS,4096);
         ::Sleep(500);//Delay for 500ms
         ClientSocket->Close();
      }
      if(Form1->spt->Checked)//Automatically deliver to serial port on local computer
      {
         StrCat(SMS,FlagBuf);
         StrCat(SMS,Form1->ComboBox1->Text.c_str());
        // SaveLog("Write Serial Port "+Form1->Port);
        // SaveLog("Start Time:"+DateTimeToStr(Now()));
        // SaveLog("End Time:"+DateTimeToStr(Now()));
         pThread=new TWriteCommThread(Form1->Port,Form1->BaudRate,(void*)buf,1000);
         pThread->Terminate();

      }
    }
    else
    {

      if(StrPas(FlagBuf).Pos("GET")) file://GPRS
      {
         if(WskStream->WaitForData(TimeOut))
         {
            WskStream->Read(buf,4096);
         }
         file://Save the flag to Flag
         strcpy(Flag,"GPRS");
         file://Save the frame to SMS
         strcpy(SMS,buf);
         file://Set the destination IP
         strcpy(RecIPAddr,"127.0.0.1");
         SaveLog("Received a package from GPRS");
         SaveLog("Start Time:"+DateTimeToStr(Now()));
         SaveLog(StrPas(FlagBuf)+StrPas(buf));
         SaveLog("End Time:"+DateTimeToStr(Now()));
         Form1->Memo1->Lines->Add(StrPas(FlagBuf)+StrPas(buf));
         FLASHWINFO FSHINFO;
         ::ZeroMemory(&FSHINFO,sizeof(FLASHWINFO));
         FSHINFO.cbSize=sizeof(FLASHWINFO);
         FSHINFO.hwnd=Application->Handle;
         FSHINFO.dwFlags=FLASHW_TRAY|FLASHW_CAPTION;
         FSHINFO.uCount=10;
         FSHINFO.dwTimeout=200;
         ::FlashWindowEx(&FSHINFO);
         if(Form1->adv->Checked)
         {
            if(StrPas(Flag).IsEmpty() || StrPas(RecIPAddr).IsEmpty() || StrPas(SMS).IsEmpty())
            {
               Application->MessageBox("You can't deliver because no data are received!","Error",MB_OK+MB_ICONERROR);
               return;
            }
            WskStream->Write(FlagBuf,5);
            WskStream->Write(Form1->ComboBox1->Text.c_str(),32);
            WskStream->Write(SMS,4096);
            ::Sleep(500);//ms
            ClientSocket->Close();
         }
         if(Form1->spt->Checked)  file://Automatically deliver to serial port on local computer
         {
             StrCat(SMS,FlagBuf);
             StrCat(SMS,Form1->ComboBox1->Text.c_str());
             file://SaveLog("Write Serial Port "+Form1->Port);
             file://SaveLog("Start Time:"+DateTimeToStr(Now()));
             file://SaveLog("End Time:"+DateTimeToStr(Now()));
             pThread=new TWriteCommThread(Form1->Port,Form1->BaudRate,(void*)SMS,4096);
             pThread->Terminate();
         }
      }
      else
      {
         int  nLen;
         int  nSize;
         int  hFile;
         char Path[255];    file://Path
         char FileName[255];      file://FileName
         char FileExt[5];        file://Extension
         static int num=0;
         file://Obtain the directory's name
         if(WskStream->WaitForData(TimeOut))
         {
            WskStream->Read(Path,255);
         }
         file://If the directory obtained from  client doesnot exist,the create it
         if(!DirectoryExists(StrPas(Path)))
         {
            CreateDir(StrPas(Path));
         }
         file://Obtain the FileName
         if(WskStream->WaitForData(TimeOut))
         {
            WskStream->Read(FileName,255);
         }
         file://Obtain the extension
         if(WskStream->WaitForData(TimeOut))
         {
            WskStream->Read(FileExt,5);
         }
         file://Obtain the client's IPAddress
         if(WskStream->WaitForData(TimeOut))
         {
            WskStream->Read(IPAddress,32);
         }
         file://Save the client's IPAddress
         strcpy(RecIPAddr,IPAddress);
         AnsiString S=StrPas(Path)+StrPas(FileName)+StrPas(FileExt);
         strcpy(buf,S.c_str());
         while(1) {
            if (FileExists(buf)) {
                  S=StrPas(Path)+StrPas(FileName)+"%03d"+StrPas(FileExt);
                  wsprintf(buf, S.c_str(),
                  num);
                  num++;
                  file://Created Susscessfully

            } else break;
         }
         hFile = FileCreate(buf);
         if (hFile==-1)
         {
            Application->MessageBox("Failed to create file on server!","Error",MB_OK+MB_ICONERROR);
            delete WskStream;
            Terminate();
            return;
         }
         file://Save the filename received from clients
         strncpy(RecFile,buf,255);
         SaveLog("Received a file:"+StrPas(buf));
         SaveLog("Client:"+StrPas(IPAddress)+"/r/nStart Time:"+DateTimeToStr(Now()));
         try {

           DWORD dwTick = GetTickCount();
            file://Obtain the length
            if (WskStream->WaitForData(
               TimeOut)) {
                  nLen = WskStream->Read(
                  &nSize, 4);
               if (nLen!=4) nSize = 0;
            }
            else
                  nSize = 0;
            file://Reading data
            for(; nSize>0 && !Terminated;
                  nSize-=nLen) {
               if (!WskStream->WaitForData(
                   5000)) {
                      if (GetTickCount()-dwTick
                     <TimeOut)
                     continue;
                     ::MessageBox(NULL,"Timeout,the thread has been terminated!","Error",MB_OK|MB_ICONERROR);
                     break;
                  }
                  nLen = WskStream->Read(buf,
                     sizeof(buf));
                  dwTick = GetTickCount();
                  if (nLen <= 0) {
                   file://Read Error
                     ::MessageBox(NULL,"Read Error,the thread has been terminated!","Error",MB_OK|MB_ICONERROR);
                     break;
                  }
                  file://Combine the data
                  FileWrite(hFile, buf, nLen);
            }
            SaveLog("Client:"+StrPas(IPAddress)+"/r/nEnd Time:"+DateTimeToStr(Now()));
            Form1->Memo1->Lines->Add("You got a file received from client,which was saved in "+StrPas(Path));
            file://Application->MessageBox("Server has successfully received the data !","Notification",MB_OK+MB_ICONINFORMATION);
            FLASHWINFO FSHINFO;
            ::ZeroMemory(&FSHINFO,sizeof(FLASHWINFO));
            FSHINFO.cbSize=sizeof(FLASHWINFO);
            FSHINFO.hwnd=Application->Handle;
            FSHINFO.dwFlags=FLASHW_TRAY|FLASHW_CAPTION;
            FSHINFO.uCount=10;
            FSHINFO.dwTimeout=200;
            ::FlashWindowEx(&FSHINFO);
            FileClose(hFile);//Read Completely


            if(Form1->adv->Checked)//Automatically Deliver
            {
               if(StrPas(Flag).IsEmpty() || StrPas(RecIPAddr).IsEmpty())
               {
                  Application->MessageBox("You can't deliver because no data are received!","Error",MB_OK+MB_ICONERROR);
                  return;
               }
               file://Send Data
               try
               {
                  buf[0]='/0';
                  hFile = -1;
                  hFile = FileOpen(StrPas(RecFile), fmOpenRead);
                  if (hFile != -1) {
                     nSize = GetFileSize((HANDLE)
                     hFile, NULL);
                     file://Send the Flag
                     WskStream->Write("FILE/0",5);
                     file://Send the name of directory
                     WskStream->Write(Path,255);
                     file://Send the filename
                     WskStream->Write(FileName,255);
                     file://Send  the extension of file
                     WskStream->Write(FileExt,5);
                     file://Send client's IP addresss
                     WskStream->Write(Form1->ComboBox1->Text.c_str(),32);
                     file://Send the length
                     WskStream->Write(&nSize, 4);
                     file://Send the data
                     for(; nSize>0; nSize-=nLen) {
                        nLen = min((int)sizeof(
                        buf), nSize);
                        nLen = FileRead(hFile, buf,
                        nLen);
                        if (nLen<=0) break;
                        WskStream->Write(buf, nLen);
                     }
               }

            } //try
            catch(Exception& e) {
               ClientSocket->Close();
               ::MessageBox(0, e.Message.c_str(),
                  "Error", MB_OK|MB_ICONERROR);
            }
            FileClose(hFile);

       }//if

     }//catch
     catch(Exception& e) {
         ClientSocket->Close();
         ::MessageBox(0, e.Message.c_str(),
         "Error", MB_ICONERROR);
     }

   }
  }
  delete WskStream;
  WskStream=NULL;
  ::Sleep(100);
}

//At ServerSocket's OnGetThread event to new a thread to communication with the requested client

void __fastcall TForm1::ServerSocket1GetThread(TObject *Sender,
      TServerClientWinSocket *ClientSocket,
      TServerClientThread *&SocketThread)
{

   SocketThread = new SrvThread(ClientSocket);

}
The metioned-written codes is used to test a hardware.It can be run correctly.As many different applications,so you only  reference it and could not be copy completely.It is important to know the principle of TServerSocket,TClientSocket and how VCL wraps the socket api.This article only provides the support of TCP protocol.The next article I will write will accomplish TCP and UDP protocol,please pay your attention for it.At this time,I would like to thank jishiping(JSP) and Raptor for their help.Thanks for browsing it.I am can be contacted via e-mail:[email protected]


以上程序可以實現服務器與客戶端之間的雙向通訊,只用一個端口,其工作原理類似於IE,由客戶端主動發起連接,服務器端在收到數據包後進行處理,然後根據已建立起的鏈路,再將數據回寫到客戶端。在同處在兩臺局域網的機器上以及一臺處於局域,而另一臺處於公網的機器上都測試通過。還有我從去年起開始接觸網絡編程,中間得到jishiping,Raptor的指點和幫助,在此再次感謝他們。

補充說明一點,我將代碼經過Notepad編輯後copy到這裏的時,發現所有的註釋部分都加了一個file:,因此當你看到file:前綴時,那就是註釋,並非代碼,特此聲明

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章