? ? 最初程序員在Unix系統下使用Berkeley Socket編寫網絡程序,隨著Windows操作系統的普及,Microsoft、Sun等公司聯合開發了Winsock接口API。它實質上是一種進 程間通信,將之從單機環境擴展到網絡環境以適合于開發主機/客戶機通信程序。網絡通信的每一端稱為一個端點,而Windows Socket為網絡端點的抽象表示。編程時,程序在網絡對話的每一端建立一個Socket(套接口),兩個Socket之間可以應用面向連接的或者是無連 接的網絡協議(不作介紹)。
? ? ? ?面向連接的網絡協議不必指定本地協議端口,只需為Socket提供遠程主機信息:IP地址和協議端口,Winsock可自動保 存本地IP地址和選擇本地端口。面向連接的Winsock客戶機/服務器模型如下圖所示,服務器程序首先用Socket函數建立一個Socket,接著服務器調用bind函數將此Socket與本地協議端口聯系起來。Listen函數將Socket置于偵聽模式,對于客戶端每個請求發 送一個確認信息,但在偵聽的Socket實際上并不接受連接請求,為了真正接受和連接請求,必須調用Accept函數。客戶端的程序也用Socket函數 建立一個Socket,客戶端通過調用connect函數啟動網絡對話。在客戶和服務器建立連接后,就可以調用write、read等函數進行進一步的通 信。
5 網絡編程的實現
? ? ? ? ?程序分為兩個應用例程:服務器端程序、客戶端程序。我們在編程時采用了MFC類庫,因為 MFC類提供了CSocket類,它封裝了Windows Sockets API,支持同步操作,簡化了Socket編程,因而降低了編程難度。
5.1 服務器例程
? ? ? ? ?其中服務器端程序主要實現:監聽客戶端的連接請求、為客戶端的連接請求建立Socket隊列、處理客戶端發送來的數據,并進行分析判斷,根據接受到的不同字符信號轉向不同的函數體,完成相應的網絡控制,并向相應的客戶端發送應答信息。
? ? ? ? ?在應用程序中需要建立兩個CSocket類的派生類:Clisten和CClient。Clisten類用于建立監聽socket,以隨時監測是否有客戶 端的連接請求。CClient類用于一旦監測到有客戶端的連接請求時,即為每一個客戶端分別建立一個socket,以便通信中不發生混亂。程序代碼如下:
CListen::Clisten(CserverdlhDlg *pdlg)
{//需要改寫Clisten類構造函數
m_pdlg=pdlg;
}
void CserverdlgDlg::OnNet()
{//右鍵菜單“網絡控制”的響應函數
m_pListening=new CListen(this);
if(m_pListening->Create(PORT))
- 3 -
{//建立監聽socket
if(!m_pListening->Listen())
{
MessageBox("port error!","net message",MB_OK);
return;
}
}
}
以下代碼利用ClassWizard生成的消息響應函數,利用了虛擬函數可以被重載的特點。
void CListen::OnAccept(int nError)
{
CClient *m_pSocket=new CClient(m_pdlg);
if(m_pdlg->m_pListening->Accept(*m_pSocket))
{// 為每一個客戶端分別建立一個socket
m_pdlg->m_pList.AddTail(m_pSocket);
}
else
delete m_pSocket;
}
? ? ? ? ?為了處理客戶端的請求,還必須重載CClient派生類的虛擬函數OnReceive(int nError),在函數體中添加代碼,對客戶發送來的信號進行分析判斷,再進一步執行相應的函數,例如:用戶發送來的代碼為“1”,執行作業文件傳輸;代碼為“2”,執行作業下載等。最后,用Send()函數向客戶端回發信息,表明成功完成網絡通信。
5.2 客戶端例程
? ? ? ? ?客戶端的程序主要實現的功能有:建立到服務器的連接、關閉與服務器的連接、向服務器發送數據(發送控制信息、傳送作業文件)、從服務器接受數據等。
? ? ? ? ?在應用程序中建立一個CSocket類的派生類:CRequest類,用于建立socket,實現客戶端與服務器端的連接通信。
CRequest::CRequest(CclientdlgDlg *pdlg)
{// 需要改寫CRequest類構造函數
m_pdlg=pdlg;
}
void CclientdlgDlg::OnOpen()
{ //打開通訊口
//以下程序實現打開連接對話框,參見圖3:服務器/客戶端界面圖:右圖。
CConnectDlg dlg1;
if(dlg1.DoModal()==IDOK)
{
m_port=dlg1.m_Port;//協議端口
servername=dlg1.m_servername;//服務器IP地址
m_pSocket=new CRequest(this);
if(!(m_pSocket=ConnectServer()))
{
…//代碼省略 - 4 -
}
}
}
CRequest* CclientdlgDlg::ConnectServer()
{
CRequest *pSocket=new CRequest(this);
if(!(pSocket->Create()))
{
…//代碼省略
}
if(!pSocket->Connect(servername,m_port))
{
…//代碼省略
}
return pSocket;//最后連接成功,返回接口對象指針
}
接著利用返回的接口對象指針pSocket來完成客戶端信息的發送,編程時,首先建立發送對話框(參見圖4),下段代碼為響應對話框發送按鈕控件程序。
void CSendDlg::OnSend()
{
UpdateData();//完成數據輸入更新
CclientdlgDlg *pdlg=(CclientdlgDlg *)AfxGetApp->m_pMainWnd;
UpdateData(FALSE);
If(pdlg->m_pSocket)
{
char pMsg[1000];
sprintf(pMsg,”%s”,m_sendedit.GetBuffer(1000);
pdlg->SendMsg(pdlg->m_pSocket,pMsg);
}
}
建立主對話框成員函數CclientdlgDlg::SendMsg(CRequest *pRequest,char *pMsg)并在函數體中調用pRequest->Send(pMsg,strlen(pMsg))完成信息的發送。
6 主控計算機與機器人控制器通信編程
? ? ? ? ?主控計算機通過標準RS232C串行接口與機器人控制柜通信,MOTOCOM32通訊軟件提供了一套動態連接的庫函數,在服務器端的應用程序中調用這些庫 函數,對機器人進行控制。這些庫函數包括了機器人的主要操作功能,例如:狀態讀取函數用于讀取機器人的位置值、當前程序名、運行方式、操作坐標、控制軸組 等狀態信息以及文件的上傳、下載等;系統控制函數可對機器人進行各種操作,包括伺服電源的通斷、程序運行的起停、機器人以各種坐標方式按不同的速度進行目 標點或者增量運動。通過這些庫函數的組合使用,實現對機器人系統的各種控制與操作,擴展了機器人的系統集成功能,利于實現機器人遠程控制和網絡控制。
6.1 動態連接庫的調用
? ? ? ? ?為了程序能夠調用動態連接庫中的庫函數,(1)主對話框的實現文件中必須包含:
- 5 -
“direct.h”、“motocom.h”頭文件 (2)復制Motocom32.DLL、Motolk.DLL、Motolkr.DLL數據傳輸動態庫到工程文件目錄下 (3)點擊工程菜單、設置子菜單,打開“link”項,在“Object/Library Module”里添加“motocom32.lib”。
6.2 通信的建立
? ? ? ? ?首先利用語句:nCid=BscOpen(adlg.jbi_path_name,1)取得通訊句柄nCid,在后續的語句中調用BscSetCom(nCid,1,9600,2,8,0)設置通訊口參數,最后進行通訊線路的連接: BscConnect(nCid)。這樣就可以進行文件的上傳(函數BscUpLoad(adlg.file_name,nCid))、下載(函數BscDownLoad(nCid,adlg.file_name))等操作了。
6.3 通訊線路的關閉
? ? ? ? ?在主對話框的OnDestroy()函數中調用關閉通訊口的函數BscClose(nCid),實現通訊的安全結束。
?
5 網絡編程的實現
?
5.1 服務器例程
?
?
CListen::Clisten(CserverdlhDlg *pdlg)
{//需要改寫Clisten類構造函數
m_pdlg=pdlg;
}
void CserverdlgDlg::OnNet()
{//右鍵菜單“網絡控制”的響應函數
m_pListening=new CListen(this);
if(m_pListening->Create(PORT))
- 3 -
{//建立監聽socket
if(!m_pListening->Listen())
{
MessageBox("port error!","net message",MB_OK);
return;
}
}
}
以下代碼利用ClassWizard生成的消息響應函數,利用了虛擬函數可以被重載的特點。
void CListen::OnAccept(int nError)
{
CClient *m_pSocket=new CClient(m_pdlg);
if(m_pdlg->m_pListening->Accept(*m_pSocket))
{// 為每一個客戶端分別建立一個socket
m_pdlg->m_pList.AddTail(m_pSocket);
}
else
delete m_pSocket;
}
?
5.2 客戶端例程
?
?
CRequest::CRequest(CclientdlgDlg *pdlg)
{// 需要改寫CRequest類構造函數
m_pdlg=pdlg;
}
void CclientdlgDlg::OnOpen()
{ //打開通訊口
//以下程序實現打開連接對話框,參見圖3:服務器/客戶端界面圖:右圖。
CConnectDlg dlg1;
if(dlg1.DoModal()==IDOK)
{
m_port=dlg1.m_Port;//協議端口
servername=dlg1.m_servername;//服務器IP地址
m_pSocket=new CRequest(this);
if(!(m_pSocket=ConnectServer()))
{
…//代碼省略 - 4 -
}
}
}
CRequest* CclientdlgDlg::ConnectServer()
{
CRequest *pSocket=new CRequest(this);
if(!(pSocket->Create()))
{
…//代碼省略
}
if(!pSocket->Connect(servername,m_port))
{
…//代碼省略
}
return pSocket;//最后連接成功,返回接口對象指針
}
接著利用返回的接口對象指針pSocket來完成客戶端信息的發送,編程時,首先建立發送對話框(參見圖4),下段代碼為響應對話框發送按鈕控件程序。
void CSendDlg::OnSend()
{
UpdateData();//完成數據輸入更新
CclientdlgDlg *pdlg=(CclientdlgDlg *)AfxGetApp->m_pMainWnd;
UpdateData(FALSE);
If(pdlg->m_pSocket)
{
char pMsg[1000];
sprintf(pMsg,”%s”,m_sendedit.GetBuffer(1000);
pdlg->SendMsg(pdlg->m_pSocket,pMsg);
}
}
建立主對話框成員函數CclientdlgDlg::SendMsg(CRequest *pRequest,char *pMsg)并在函數體中調用pRequest->Send(pMsg,strlen(pMsg))完成信息的發送。
6 主控計算機與機器人控制器通信編程
?
6.1 動態連接庫的調用
?
- 5 -
“direct.h”、“motocom.h”頭文件 (2)復制Motocom32.DLL、Motolk.DLL、Motolkr.DLL數據傳輸動態庫到工程文件目錄下 (3)點擊工程菜單、設置子菜單,打開“link”項,在“Object/Library Module”里添加“motocom32.lib”。
6.2 通信的建立
?
6.3 通訊線路的關閉
?