11.2.2 功能实现(1)
在本节中,将向用户讲解服务器端以及客户端的各个功能函数的实现方法。在这些功能函数中,对数据结构的使用同样也是非常重要的一个知识点。
1.服务器功能实现
首先,当服务器启动时会在指定端口等待客户端的连接。如果连接成功,则将在服务器端列表控件中显示客户端的相关信息。代码如下:
- void CData::bind()
- {
- WSADATA data; //定义结构体变量
- CString name; //定义主机名字符串
- DWORD ss=MAKEWORD(2,0); //指定套接字版本
- ::WSAStartup(ss,&data); //初始化套接字库
- s=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
- //创建套接字
- sockaddr_in addr; //定义地址结构变量
- ::gethostname((char*)&name,(int)sizeof(name)); //获得主机名字
- hostent *p=::gethostbyname((char*)&name); //从主
机名获取主机地址 - in_addr *a=(in_addr*)*p->h_addr_list; //获得
本机IP地址 - CString str14=::inet_ntoa(a[0]); //转
换字符串IP地址 - addr.sin_family=AF_INET; //填充地址结构
- addr.sin_port=htons(80);//指定监听端口为80
- addr.sin_addr.S_un.S_addr=inet_addr(str14);//指定主机IP地址
- ::bind(s,(sockaddr*)&addr,sizeof(addr)); //将
本地信息绑定到套接字 - ::listen(s,5); //监听
- WSAAsyncSelect(s,this->m_hWnd,WM_SOCK,FD_ACCEPT|FD_READ);
- //将套接字设置为异步模式
- }
在程序中,用户使用函数gethostname()获取本地主机名,并且通过该主机名得到相应的IP地址信息。再将本地主机的相关信息绑定到创建的套接字上,并将该套接字设置为异步模式。
注意:在本节中向用户讲解的各个功能函数均为自定义类CData的成员函数。关于该类的具体封装方法将在11.2.3节中具体讲解。
然后,用户在程序中必须定义与套接字消息相应的响应函数,并且使用消息映射宏将套接字消息WM_SOCK与其响应函数相关联才能实现套接字的异步模式。用户为套接字消息添加消息映射,代码如下:
- BEGIN_MESSAGE_MAP(CQQDlg, CDialog)
- //{{AFX_MSG_MAP(CQQDlg)
- ON_WM_SYSCOMMAND()
- ON_WM_QUERYDRAGICON()
- ... //省略部分消息映射项
- ON_MESSAGE(WM_SOCK,Onsoc) //套接字消息映射
- ON_WM_CTLCOLOR()
- ON_WM_TIMER()
- ON_WM_LBUTTONDOWN()
- //}}AFX_MSG_MAP
- END_MESSAGE_MAP()
在消息映射宏中,用户可以看到与套接字消息相关联的函数是Onsoc()。该函数的声明代码如下:
- class CQQDlg : public CDialog
- {
- public:
- ... //省略部分代码
- afx_msg void Onsoc(WPARAM wParam,LPARAM lParam); //声
明套接字消息响应函数 - }
用户对套接字消息响应函数声明以后,便可以在程序中实现该函数。代码如下:
- int i=0;//定义全局变量
- qqstruct qq; //定义结构体qqstruct变量
- qqmsg msg[5]; //定义结构体qqmsg变量
- void CQQDlg::Onsoc(WPARAM wParam,LPARAM lParam)
- {
- switch (lParam) //判断套接字事件
- {
- case FD_ACCEPT: //套接字连接事件
- {
- msg[i].s=::accept(s,NULL,NULL); //保存
客户端连接时返回的套接字 - if(i<5) //判断连接的客户端是否已满
- {
- i+=1;
- }
- else
- {
- i=0;
- }
- }
- break;
- case FD_READ: //套接字读取事件
- {
- recv(msg[i].s, msg[i].qq,sizeof(msg[i].qq),NULL);
- //接收相应套接字上的消息
- int nRow=m_list.InsertItem(m_list.GetItemCount()+1,
msg[i].qq.m_name); - //向列表控件中插入行数据
- m_list.SetItemText(nRow,1, msg[i].qq .m_addr); //设置数据
- }
- break;
- default: break;
- }
- }