作者:袁 渊(华东船舶工业学院机械系) 转载请与作者联系 三、具体实施 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,通过与结构体列表比较,确定需要恢复的发送线程(所有发送线程在创建时都被挂起)。这样,服务器就准确地转了发数据。 |