一. 前言
目前網絡編程技術有很多,一般都是直接利用編程軟件自帶的功能函數或控件來完成,用戶不必關心低層的操作,確實帶來方便。但靈活性不夠,如果要傳輸的文件比較大,則速度慢。如果網絡的斷線率高,則不得不重新傳輸。本程序可以克服以上不足,將任意大小的文件視網絡狀況在客戶端分割成任意大小的數據流進行發送,在服務端接收數據流,並組裝成原文件。如果網絡被中斷,則記下位置,待網絡恢復後繼續從斷點的位置傳輸,終端繼續接收,直到傳輸完畢。
本程序在Windows2000調試通過,讀者可以應用本程序的設計技術應用到其他網絡通訊中去。
二.建立程序框架
新建一個工程存為tongxun.bpr,Form文件存為shi.cpp,對Form的屬性做如下修改:BorderStyle設為bsSingle,Name設為Form,Position設為PoScreenCenter。在Form上放置2個Tbutton組件,分別為:Name分別為Button1和Button2,Caption分別為"發送圖片"和"選擇圖片文件",放置1個Timage組件,Name為Image1,放置1個Opendialog,Name為name,用於打開1個文件,最後放置1個TserverSocket和1個Tclientsocket,Name屬性分別為server和client。
這樣就完成了程序框架的建立,如圖示:
三.程序實現
本程序是在客戶端發送一個2M字節的BMP圖片,終端接收和組裝此圖片並顯示在Form的窗口中用於驗證。本程序即可作為發送端,也可做接收端,只要在兩台互聯的計算機上運行即可。
shi.cpp源文件如下:
#include
#pragma hdrstop
#include "Unit1.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
static int shi=0,emod=0,hui=0,x=0,a=0,i=0;
AnsiString filename;
file://---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{ }
file://---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{ client->Active=false;
client->Port=8888;
client->Active=true;} file://聯接遠程計算機
file://---------------------------------------------------------------------------
void __fastcall TForm1::clientConnect(TObject *Sender,TCustomWinSocket *Socket)
{ label->Caption="ok";} file://聯接成功的提示。
file://下面的函數用於接收數據和組裝數據並顯示。
void __fastcall TForm1::serverClientRead(TObject *Sender,
TCustomWinSocket *Socket)
{ // static long a=0;
static TMemoryStream* pms = new TMemoryStream();
void *z[20000]; file://定義緩沖區大小
if(shi!=1)
{Socket->ReceiveBuf(z,20000); file://接收SOCKET中的數據流
pms->Position=a;
pms->WriteBuffer(z,20000); file://寫入緩沖區
a=a+20000; file://改變內存流指針
client->Active=false; file://每次發送前要判斷網絡是否暢通
client->Port=8888; file://聯接遠程計算機,如果成功則
client->Active=true;} file://發送,否則等待。
Else file://接收最後emod個字節。
{pms->WriteBuffer(z,emod);
pms->SaveToFile("d:\\k.bmp");
label->Caption="successful";
pms->Position=0;
Image1->Picture->Bitmap->LoadFromStream(pms);}
} file://將接收的文件在屏幕上顯示。
(圖2)發送前
(圖3)接收後顯示
file://以下函數用於任意分割文件和發送文件。
void __fastcall TForm1::clientWrite(TObject *Sender,TCustomWinSocket *Socket)
{ void *p[20000];
static long k=20000;//x=0;//hui=0;
static long bmpsize,/*i=0*/c,mod;
static TMemoryStream* pms = new TMemoryStream();
if(hui==0){pms->LoadFromFile(filename);hui++;}//裝入要發送的文件。
bmpsize=pms->Size; file://計算文件的大小(字節)
c=(int)bmpsize/k; file://分割文件為c
mod=bmpsize%k; file://分割後於下的字節數
emod=mod; file://使mod在整個程序都可訪問
if(i {pms->Position=x; file://改變內存流讀取指針
pms->ReadBuffer(p,k); file://把內存流讀入緩沖區,即改變流的類型
x=x+k;
client->Socket->SendBuf(p,k); file://將分割的文件發送
i++; }
else
{ shi++;
if(shi==1)
{pms->ReadBuffer(p,mod); file://發送最後mod個字節後,發送完畢
client->Socket->SendBuf(p,mod); }}}
//下面函數用於選擇要發送的文件(*.bmp)
void __fastcall TForm1::Button2Click(TObject *Sender)
{ if(OpenDialog1->Execute()){
filename = OpenDialog1->FileName;
label->Caption="選擇圖片成功";
hui=0; shi=0; a=0;
x=0; i=0;}}
file://---------------------------------------------------------------------------
由於在文件中要用到全局變量gan,所以要在unit.h的頭文件中加入其定義,如下:
class TForm1 : public TForm
{private: …
TMemoryStream* gan;
…};
四.技術關鍵
把文件進行分割成一塊一塊的內存流,由於c++builder的套接字Socket在發送接收時是以緩沖區類型的為數據,因此需要把內存流轉化成此類型數據傳輸。為了使在網絡斷線時保存斷點,用了全局變量和函數內部的靜態變量巧妙的解決了這個問題。