ITPub博客

首页 > 应用开发 > IT综合 > 用C++ Builder中的TServerSocket,TClientSocket来写网络通讯程序 (转)

用C++ Builder中的TServerSocket,TClientSocket来写网络通讯程序 (转)

原创 IT综合 作者:gugu99 时间:2007-08-16 08:10:46 0 删除 编辑
用C++ Builder中的TServerSocket,TClientSocket来写网络通讯程序 (转)[@more@]

本文主要介绍如何在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()
{

  Text or SendFile
  UINT TimeOut=60000;
  char buf[4096];
  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",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]='';
  [0]='';
  WskStream->Read(FlagBuf,5);
  WskStream->Read(IPAddress,32);
  int nSize=0;
  nSize=WskStream->Read(buf,TxtLen);
  buf[nSize]='';
  if(!StrPas(buf).IsEmpty())
  {
  SaveLog("Received a text!");
  ("Client:"+StrPas(IPAddress)+" Start 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));

  ("Client:"+StrPas(IPAddress)+" End 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);
  the Flag
  WskStream->Write("FILE",5);
  the name of directory
  WskStream->Write(Path,255);
  the filename
  WskStream->Write(FileName,255);
    the extension of file
  WskStream->Write(FileExt,5);
  client's IP addresss
  WskStream->Write(IPAddress,32);

  the length
  WskStream->Write(&nSize, 4);
  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);
  }
  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]='';
  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)+" Start 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;
  data
  for(; nSize>0 && !Terminated;
  nSize-=nLen) {
  if (!WskStream->WaitForData(
  5000)) {
  if (GetTickCount()-dwTick
    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) {
  Error
  ClientSocket->Active=false;
  ::MessageBox(NULL,"Read Error,the thread has been terminated!","Error",MB_OK|MB_ICONERROR);
  break;
  }
  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)+" End 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;
}

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)  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  File
  {
  if (OpenDialog1->Execute())
  new ClientThread(Addr, Port,
  OpenDialog1->FileName); to send data
  }
}
Server:
.h File
Comments:
//At design-time,please place a TServerSocket Component on your foRM and set its clientype to stThreadblocking.
Thread Class
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; Seconds
  WskStream = new TWinSocketStream(ClientSocket, TimeOut);
  Text or File
  char FlagBuf[5];
  char buf[4096];
  char IPAddress[32];
  SMS[0]='';
  RecIPAddr[0]='';
  if(WskStream->WaitForData(TimeOut)) file://Obtain Flag:File or Text
  {
  WskStream->Read(FlagBuf,5);
  }
  the flag received from clients
  strcpy(Flag,FlagBuf);
  if(StrPas(FlagBuf)=="TEXT")
  {

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

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

  SaveLog("Received a text!");
  SaveLog("Client:"+StrPas(IPAddress)+" Start Time:"+DateTimeToStr(Now()));
  SaveLog("Text Content:"+StrPas(SMS));
  SaveLog("Client:"+StrPas(IPAddress)+" End 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"))
  {
  if(WskStream->WaitForData(TimeOut))
  {
  WskStream->Read(buf,4096);
  }
  the flag to Flag
  strcpy(Flag,"GPRS");
  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());
  ("Write Serial Port "+Form1->Port);
  ("Start Time:"+DateTimeToStr(Now()));
  ("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]; 
  char FileExt[5];  file://Extension
  static int num=0;
  file://Obtain the directory's name
  if(WskStream->WaitForData(TimeOut))
  {
  WskStream->Read(Path,255);
  }
  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);
  }
  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;
  }
  the filename received from clients
  strncpy(RecFile,buf,255);
  SaveLog("Received a file:"+StrPas(buf));
  SaveLog("Client:"+StrPas(IPAddress)+" Start 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;
  data
  for(; nSize>0 && !Terminated;
  nSize-=nLen) {
  if (!WskStream->WaitForData(
  5000)) {
  if (GetTickCount()-dwTick
    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) {
    Error
  ::MessageBox(NULL,"Read Error,the thread has been terminated!","Error",MB_OK|MB_ICONERROR);
  break;
  }
  the data
  FileWrite(hFile, buf, nLen);
  }
  SaveLog("Client:"+StrPas(IPAddress)+" End 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;
  }
  Data
  try
  {
  buf[0]='';
  hFile = -1;
  hFile = FileOpen(StrPas(RecFile), fmOpenRead);
  if (hFile != -1) {
  nSize = GetFileSize((HANDLE)
  hFile, NULL);
  the Flag
  WskStream->Write("FILE",5);
  the name of directory
  WskStream->Write(Path,255);
  the filename
  WskStream->Write(FileName,255);
    the extension of file
  WskStream->Write(FileExt,5);
  client's IP addresss
  WskStream->Write(Form1->ComboBox1->Text.c_str(),32);
  the length
  WskStream->Write(&nSize, 4);
  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);

}
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 VCL wraps the socket api.This article only provides the support of TCP protocol.The next article I will write will 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 :kingcaiyao@163.com">E-mail:kingcaiyao@163.com


以上程序可以实现服务器与客户端之间的双向通讯,只用一个端口,其工作原理类似于IE,由客户端主动发起连接,服务器端在收到数据包后进行处理,然后根据已建立起的链路,再将数据回写到客户端。在同处在两台局域网的机器上以及一台处于局域,而另一台处于公网的机器上都测试通过。还有我从去年起开始接触网络编程,中间得到jishiping,Raptor的指点和帮助,在此再次感谢他们。

补充说明一点,我将代码经过Notepad编辑后copy到这里的时,发现所有的注释部分都加了一个file:,因此当你看到file:前缀时,那就是注释,并非代码,特此声明

 


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/10748419/viewspace-960511/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
  • 博文量
    3121
  • 访问量
    2644375