- 客户端的代码:(这份test_client的代码也会为后续的I/O模型做测试)
#include<WinSock2.h>
#include<WS2tcpip.h>
#include<stdlib.h>
#include<stdio.h>
#include<iostream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
#define DEF_PORT 27015
#define DEF_SIZE 512
int main()
{
WSADATA wsaData;
SOCKET ConnectSocket = INVALID_SOCKET;
int iResult;
//初始化Winsock
iResult = WSAStartup(MAKEWORD(2,2),&wsaData);
if(iResult != 0){
cout<<"WSAStartup failed with error: "<<iResult<<endl;
return 1;
}
//创建用于连接的socket
ConnectSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
if(ConnectSocket == INVALID_SOCKET){
cout<<"socket created with error: "<<GetLastError()<<endl;
WSACleanup();
return 1;
}
//连接
sockaddr_in addserver;
addserver.sin_family = AF_INET;
addserver.sin_port = htons(DEF_PORT);
addserver.sin_addr.S_un.S_addr = inet_addr("10.50.92.211");
//连接
iResult = connect(ConnectSocket,(const sockaddr*)&addserver,sizeof(addserver));
if(iResult != 0){
printf("connect failed with error :%d\n",GetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
char sendbuf[DEF_SIZE];
printf("conntect successful...\n");
printf("Please input sendbuf:");
cin>>sendbuf;
//发送数据
iResult = send(ConnectSocket,sendbuf,sizeof(sendbuf),0);
if(iResult > 0){
printf("host send: %s\n",sendbuf);
}
closesocket(ConnectSocket);
WSACleanup();
system("pause");
return 0;
}
- 使用阻塞I/O的服务器代码:
#include<WinSock2.h>
#include<WS2tcpip.h>
#include<stdlib.h>
#include<stdio.h>
#include<iostream>
using namespace std;
#pragma comment (lib,"ws2_32.lib")
#define DEF_PORT 27015
#define DEF_SIZE 512
int main()
{
WSADATA wsaData;
int iResult;
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET AcceptSocket = INVALID_SOCKET;
//1.初始化
iResult = WSAStartup(MAKEWORD(2,2),&wsaData);
if(iResult != 0)
{
printf("WSAStartup failed with error :%d\n",iResult);
return 1;
}
//2.创建用于监听的套接字
ListenSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
if(ListenSocket == INVALID_SOCKET){
printf("create ListenSocket failed with error:%d\n",GetLastError());
WSACleanup();
return 1;
}
//3.为监听套接字绑定地址和端口号
sockaddr_in addServer;
addServer.sin_family = AF_INET;
addServer.sin_port = htons(DEF_PORT);
addServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
iResult = bind(ListenSocket,(const sockaddr*)&addServer,sizeof(addServer));
if(iResult == SOCKET_ERROR){
printf("bind failed with error:%d",GetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
/*监听客户端的连接
如果设置为SOMAXCONN,则负责套接字的底层服务提供程序会将backlog设置为最大合理值。*/
iResult = listen(ListenSocket,SOMAXCONN);
if(iResult == SOCKET_ERROR){
printf("Listen failed with error:%d\n",GetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
printf("TCP Server is starting...\n");
sockaddr_in addClient;
int addClient_Size = sizeof(addClient);
char recvbuf[DEF_SIZE] = {0};
int recvlen = sizeof(recvbuf);
//循环等待客户请求建立连接,并处理连接请求
while(1){
AcceptSocket = accept(ListenSocket,(sockaddr*)&addClient,&addClient_Size);
if(AcceptSocket == INVALID_SOCKET){
printf("accpet failed!\n");
closesocket(ListenSocket);
WSACleanup();
return 1;
}
printf("收到新的连接:%s\n",inet_ntoa(addClient.sin_addr));
//循环接收数据
while(1){
memset(recvbuf,0,sizeof(recvbuf));
iResult = recv(AcceptSocket,recvbuf,recvlen,0);
if(iResult > 0){
printf("数据内容: %s\n",recvbuf);
continue;
}
else if(iResult == 0){ //返回值0代表正常关闭
printf("客户端已经正常关闭....\n");
closesocket(AcceptSocket);
break;
}
else{ //接收出现错误
printf("recv failed with error: %d\n",GetLastError());
closesocket(AcceptSocket);
closesocket(ListenSocket);
WSACleanup();
return 1;
}
}
}
closesocket(AcceptSocket);
closesocket(ListenSocket);
WSACleanup();
system("pause");
return 0;
}
- 测试结果:
- 模型评价:
阻塞I/O模型是网络通信中最常用的一种,优点是简单直接,缺点就是在处理多个套接字连接的时候,串行处理多套接字的I/O操作会导致处理时间延长、程序的执行效率降低。
这种情况下我们关心的是如何及时处理多个I/O请求,有两种解决方案:
(1)使用多线程并发处理多个I/O请求,只要来一个I/O请求,就生成一个线程去解决,但是会增加线程的启动和终止,以及维护线程运行时间和同步操作。而且个人觉得线程数量太多也是一种资源的消耗。增大了系统开销和程序的复杂度
(2)异步、非阻塞的处理多个I/O请求,也就是之后的I/O模型