#include<iostream>
#include<WinSock2.h>
using namespace std;
#pragma comment(lib,"Ws2_32.lib")
const int nDefaultServerPort=10000;
const int buf_len=1024;
SOCKET AcceptConnection(SOCKET sdListen);
bool ProcessConnection(SOCKET sd);
bool ShutdownConnection(SOCKET sd);
SOCKET BindListen();
void DoWork();
int main()
{
WSAData wsaData;
int nCode;
if((nCode=WSAStartup(MAKEWORD(2,2),&wsaData))!=0)
{
cout<<"WSAStartup error"<<nCode<<endl;
return -1;
}
DoWork();
WSACleanup();
return 0;
}
void DoWork()
{
//获取监听套接字并进入监听状态
SOCKET sdListen=BindListen();
if(sdListen==INVALID_SOCKET)
{
return;
}
//服务器主循环
while(true)
{
SOCKET sd=AcceptConnection(sdListen);
if(sd==INVALID_SOCKET)
{
break;
}
//第二阶段,服务一个客户端连接
if(ProcessConnection(sd)==false)
{
break;
}
//第三阶段,关闭一个客户端连接
if(ShutdownConnection(sd)==false)
{
break;
}
//关闭监听套接字
if(closesocket(sdListen)==SOCKET_ERROR)
{
cout<<"closesocket error"<<WSAGetLastError()<<endl;
}
}
}
SOCKET BindListen()
{
//创建一个监听套接字
SOCKET sd=socket(AF_INET,SOCK_STREAM,0);
if(sd==INVALID_SOCKET)
{
cout<<"socket error"<<WSAGetLastError()<<endl;
return INVALID_SOCKET;
}
//填充本地套接字
//这里使用了通配地址INADDR_ANY。当然也可以指明一个具体的本地IP地址,但是如果使用通配地址
//我们可以接受来自所有网络接口的连接请求。这对于带有多个网卡的服务器来说可以简化编程
sockaddr_in saListen;
saListen.sin_family=AF_INET;
saListen.sin_addr.s_addr=htonl(INADDR_ANY);
saListen.sin_port=htons(nDefaultServerPort);
//调用bind把本地套接字地址绑定到监听套接字
if(bind(sd,(sockaddr*)&saListen,sizeof(sockaddr_in))==SOCKET_ERROR)
{
cout<<"bind error"<<WSAGetLastError()<<endl;
closesocket(sd);
return INVALID_SOCKET;
}
//开始监听
if(listen(sd,5)==SOCKET_ERROR)
{
cout<<"listen error"<<WSAGetLastError()<<endl;
closesocket(sd);
return INVALID_SOCKET;
}
return sd;
}
//接受一个客户端连接并返回对应于该连接的套接字句柄
SOCKET AcceptConnection(SOCKET sdListen)
{
sockaddr_in saRemote;
int nSize=sizeof(sockaddr_in);
SOCKET sd=accept(sdListen,(sockaddr*)&saRemote,&nSize);
if(sd==INVALID_SOCKET)
{
cout<<"accept error"<<WSAGetLastError()<<endl;
}
return sd;
}
//服务一个客户端连接,实现回显服务业务逻辑
bool ProcessConnection(SOCKET sd)
{
char buff[buf_len];
int nRecv;
//循环直到客户端关闭数据连接
do{
//接收客户端的数据
//由于套接字sd是阻塞模式,对其调用recv将会阻塞,直到recv完成返回。当
//recv返回0时,表明客户端完成数据发送并且关闭了连接,此时就可以退出循环
nRecv=recv(sd,buff,buf_len,0);
if(nRecv==SOCKET_ERROR)
{
cout<<"recv error"<<WSAGetLastError()<<endl;
return false;
}
else if(nRecv>0)
{
int nSent=0;
//把数据原封不动发回客户端,即回显
while(nSent<nRecv)
{
//这里的send也会阻塞,只有当send返回后,程序才能继续执行
int nTemp=send(sd,&buff[nSent],nRecv-nSent,0);
if(nTemp>0)
{
nSent+=nTemp;
}
else if(nTemp==SOCKET_ERROR)
{
cout<<"send error"<<WSAGetLastError()<<endl;
return false;
}
else
{
//send返回0,由于此时nSent<nRecv,也就是说还有数据没有发送出错,所以连接是被客户端意外关闭的
cout<<"Connection closed unexpectedly by peer"<<endl;
return true;
}
}
}
}while(nRecv!=0);
cout<<"Connection closed by peer"<<endl;
return true;
}
//安全关闭一个TCP连接
bool ShutdownConnection(SOCKET sd)
{
//首先发送一个TCP FIN分段,向对方表明已经完成数据发送
if(shutdown(sd,SD_SEND)==SOCKET_ERROR)
{
cout<<"shutdown error"<<WSAGetLastError()<<endl;
return false;
}
char buff[buf_len];
int nRecv;
//继续接受对方的数据,直到recv返回0为止
do{
nRecv=recv(sd,buff,buf_len,0);
if(nRecv==SOCKET_ERROR)
{
cout<<"recv error"<<WSAGetLastError()<<endl;
return false;
}
else if(nRecv>0)
{
cout<<nRecv<<"unexcepted bytes received"<<endl;
}
}while(nRecv!=0);
if(closesocket(sd)==SOCKET_ERROR)
{
cout<<"closesocket error"<<WSAGetLastError()<<endl;
return false;
}
return true;
}