作者:袁 淵(華東船舶工業學院機械系)
轉載請與作者聯系
三、具體實施
3.1 注冊/登陸服務器
注冊/登陸服務器程序是基於對話框的,該程序使用I/O端口56789與用戶端連接。
首先,在對話框初始化的同時完成網絡初始化,即執行Init_net()函數,代碼(不完整)如下:
BOOL CServerDlg::Init_net()
{////////////////////////網絡初始化///////////////////////////////
addrLen=sizeof(SOCKADDR_IN);
status=WSAStartup(MAKEWORD(1, 1), &Data);
………
memset(&serverSockAddr, 0, sizeof(serverSockAddr));
/*以下指定一個與某個SOCKET連接本地或遠程地址*/
serverSockAddr.sin_port=htons(PORT);
serverSockAddr.sin_family=AF_INET;
serverSockAddr.sin_addr.s_addr=htonl(INADDR_ANY);
serverSocket=socket(AF_INET, SOCK_STREAM, 0);//初始化SOCKET
………
status=bind(serverSocket,(LPSOCKADDR)&serverSockAddr,sizeof(serverSockAddr));
//將SOCKET與地址綁定
………
status=listen(serverSocket, 5); //開始監聽
………
return true;
}
接著按下RUN鍵開始服務器功能,執行Reg_Load()函數,使服務器始終處於等待連接狀態,但這樣也使該線程始終阻塞。當有用戶連接時,該函數創建一個任務用於處理與用戶及數據庫的事務。具體任務函數略(詳見原始代碼文件)。
void CServerDlg::Reg_Load()
{
while(1)
{
CWinThread* hHandle;
clientSocket=accept(serverSocket,(LPSOCKADDR)&clientSockAddr,&addrLen);
//等待連接,阻塞
hHandle=AfxBeginThread(talkToClient,(LPVOID)clientSocket);//有連接時,創建任務
………
}
}
任務函數在接收到消息時,要對數據庫進行操作,由於數據庫較簡單,采用ODBC連接ACCESS數據庫(將netuser.mdb在ODBC數據管理器中安裝成同名數據源)具體代碼略。
3.2 通信服務器
通信服務器是本程序實現的關鍵,它運用共享數據結構技術及多線程技術,通過I/O端口56790與用戶端連接,實現了數據轉發功能。首先,程序初始化網絡Init_net(),接著當用戶連接到服務器時,創建接收線程和發送線程,這樣就可以實現數據轉發。最後,當用戶斷開連接時,服務器關閉與他的連接,並結束相應的線程。
下面我們來看一下本程序中的共享數據結構的具體內容與使用方法以及多線程的相關內容與實現。
● 共享數據結構
本程序的共享數據結構一共有兩個,即socket_info和send_info。前者包含了所有登陸用戶的一些基本資料,後者則包含了當前服務器接收到的用戶端所發送的信息資料。詳細內容及注釋如下:
struct socket_info
{
SOCKET s_client; //用戶的SOCKET值
u_long client_addr; //用戶網絡地址
CString pet; //用戶昵稱
CWinThread* thread; //為該用戶創建的發送線程對象的指針
};
struct send_info
{
CString data; //用戶端發送的數據內容(經過編輯)
CWinThread* thread; //需要發送數據的任務指針
};
在程序中,定義兩個全局變量,用來在線程間共享: send_info info_data; CList< socket_info,socket_info&
>s_info;
每當有用戶連接到服務器,服務器就將用戶端的一些信息以socket_info結構體的形式存入s_info列表中;而當服務器接收到用戶端發送過來的數據時,就將數據格式化後存入結構體info_data,通過與結構體列表比較,確定需要恢復的發送線程(所有發送線程在創建時都被掛起)。這樣,服務器就准確地轉了發數據。