#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; }