1.实验目的
1.1掌握Winsock I/O模型工作原理;
1.2熟悉I/O模型中使用的Winsock接口函数;
1.3掌握使用I/O模型进行网络程序设计的编程步骤;
2.实验内容
2.1在上述I/O模型中自选一个I/O模型,构建一个TCP服务器,该服务器能:
① 接受客户端连接时显示客户端的IP,PORT信息
②接收客户端连接时显示其连接编号,客户端退出时显示关闭的连接编号
③能显示客户端发来的数据
④能从键盘输入数据并发到客户端
⑤其他数据传送功能(可选)
2.2 编写客户端程序,使之能:
①从键盘输入数据并发送到服务器
②能接收服务器发来的数据
③当输入“bye”时退出程序
3.实验设计
由实现指导书我们不难看出,这次的任务与理论课实验二多线程非阻塞通信类似,都是写一个客户端,一个服务端,实现两端的远程通信功能,要求服务端能同时接受多个客户端的连接请求,并能向客户端发送消息,不同的是,我们需要加入本课程的核心内容:五种通信模型,选择模型(select),异步选择模型(WSAAsyncSelect),事件选择模型(WSAEventSelect),重叠I/O模型,完成端口模型。
同时,我们之前对普通的非阻塞通信服务器实现多客户端同时连接靠的是多线程,但这次我们可以靠模型函数来实现,无需写复杂的多线程(至少前面四个模型不需要写),如果你只想写选择模型,这次的是实验是相对来说更简单的
目前我还只会选择模型和事件选择模型,我后面会逐步更新
首先看选择模型:
select(选择模型)服务端代码:
#include <winsock2.h>
#include <iostream>
#include <string>
#define BUF_size 999
int main()
{
WSADATA wsa;
WSAStartup(MAKEWORD(2,2),&wsa);
SOCKET host_ser;
SOCKET connect;
host_ser = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
ULONG mode = 1;
ioctlsocket(host_ser,FIONBIO,&mode);
SOCKADDR_IN addr_ser;
addr_ser.sin_family = AF_INET;
addr_ser.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addr_ser.sin_port = htons(9990);
bind(host_ser,(const struct sockaddr*)&addr_ser,sizeof(SOCKADDR_IN));
TIMEVAL timeout;
timeout.tv_sec= 1;
FD_SET fd_read;
listen(host_ser,SOMAXCONN);
printf("等待连接中...\n");
FD_ZERO(&fd_read);
FD_SET(host_ser,&fd_read);
int ret = select(0,&fd_read,NULL,NULL,NULL);
int amount = 0;
while (true)
{
if(ret != amount )
{
amount = ret;
SOCKADDR_IN addr_connect;
int addrlenth = sizeof(addr_connect);
connect = accept(host_ser,(SOCKADDR FAR*)&addr_connect,&addrlenth);
printf("收到连接(%s) 端口:%d \n",inet_ntoa(addr_connect.sin_addr),addr_connect.sin_port);
while (true)
{
char buffer[BUF_size];
int ret_recv = recv(connect, buffer, BUF_size, 0);
if(ret_recv > 0)
{
printf("客户端消息:%s \n",buffer);
}
else if(ret_recv < 0)
{
Sleep(500); //给客户端反应时间,减少循环次数,可以不加
}
if(strcmp(buffer,"bye") == 0)
{
printf("连接结束\n");
break;
}
}
break;
}
}
closesocket(host_ser);
WSACleanup();
system("pause");
return 0;
}
select(选择模型)客户端代码:
#include <winsock2.h>
#include <iostream>
#include <string>
#define BUF_size 999
int main()
{
WSADATA wsa;
WSAStartup(MAKEWORD(2,2),&wsa);
SOCKET host_client;
host_client = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
ULONG mode = 1;
ioctlsocket(host_client,FIONBIO,&mode);
SOCKADDR_IN addr_client;
addr_client.sin_family = AF_INET;
addr_client.sin_addr.S_un.S_addr = inet_addr("IP"); //改IP!
addr_client.sin_port = htons(9990);
TIMEVAL timeout;
FD_SET fd_write;
connect(host_client, (LPSOCKADDR)&addr_client,sizeof(addr_client));
FD_ZERO(&fd_write);
FD_SET(host_client,&fd_write);
int ret = select(0,NULL,&fd_write,NULL,NULL);
char buffer[BUF_size];
if(ret > 0)
{
while(true)
{
printf("请输入消息:");
ZeroMemory(buffer, BUF_size); //如果用scanf,无法输入带空格的消息,但用gets会导致前后消息连起来,需要用此函数清空缓存区
gets(buffer);
int ret_send = send(host_client, buffer, strlen(buffer), 0);
if (SOCKET_ERROR == ret_send)
{
int err = WSAGetLastError();
if (err == WSAEWOULDBLOCK)
{
Sleep(500);
continue;
}
else
{
printf("发送失败 !\n");
}
}
}
}
closesocket(host_client);
WSACleanup();
system("pause");
return 0;
}