如题:
/*
参考博客:http://blog.csdn.net/zjsiva/article/details/5895087
*/
#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
#include <WINSOCK2.H>
#pragma comment(lib,"ws2_32.lib")
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA WSAData;
SOCKET ServerSocket;
struct sockaddr_in ServerAddr;
/*
MAKEWORD定义是这样的:
#define MAKEWORD(a, b) \
((WORD) (((BYTE) (a)) | ((WORD) ((BYTE) (b))) << 8))
WSAStartup函数的第一个参数指明程序请求使用的Socket版本,其中高位字节指明副版本、低位字节指明主版本;
操作系统利用第二个参数返回请求的Socket的版本信息。
*/
if(WSAStartup(MAKEWORD(2,2),&WSAData)!=0)
{
printf("Init windows socket failed::%d\n",GetLastError());
return 1;
}
ServerSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(ServerSocket==INVALID_SOCKET)
{
printf("Create socket failed::%d\n",GetLastError());
WSACleanup();
return 1;
}
ServerAddr.sin_family=AF_INET;
ServerAddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
ServerAddr.sin_port=htons(10086);
int iResult;
bool bReuseAddr=true; /* bReuseAddr=0x01 */
/*
第二个参数是被设置的级别,如果想要在套接字级别上设置选项,就必须把参数设置为SOL_SOCKET
SO_REUSEADDR提供如下四个功能:
SO_REUSEADDR允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将此端口用做他们的本地端口的连接
仍存在。这通常是重启监听服务器时出现,若不设置此选项,则bind时将出错。
SO_REUSEADDR允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。对于TCP,
我们根本不可能启动捆绑相同IP地址和相同端口号的多个服务器。
SO_REUSEADDR允许单个进程捆绑同一端口到多个套接口上,只要每个捆绑指定不同的本地IP地址即可。这一般不用于
TCP服务器。
SO_REUSEADDR允许完全重复的捆绑:当一个IP地址和端口绑定到某个套接口上时,还允许此IP地址和端口捆绑到另一
个套接口上。一般来说,这个特性仅在支持多播的系统上才有,而且只对UDP套接口而言(TCP不支持多播。
*/
iResult=setsockopt(ServerSocket,SOL_SOCKET,SO_REUSEADDR,(char *)&bReuseAddr,sizeof(bReuseAddr));
if(SOCKET_ERROR==iResult)
{
printf("Failed to set reuseaddr socket::%d\n",GetLastError());
return 1;
}
if(bind(ServerSocket,(struct sockaddr *)&ServerAddr,sizeof(ServerAddr))!=0)
{
printf("Bind socket failed::%d\n",GetLastError());
return 1;
}
if(0!=listen(ServerSocket,5))
{
printf("Failed to listen client::%d\n",GetLastError());
WSACleanup();
return 1;
}
/*
typedef struct fd_set {
u_int fd_count; // how many are SET?
SOCKET fd_array[FD_SETSIZE]; // an array of SOCKETs
} fd_set;
*/
fd_set fd;
//将套接字集合清空
FD_ZERO(&fd);
FD_SET(ServerSocket,&fd);
printf("Start server...\n");
u_int i;
SOCKET acceptSocket;
sockaddr_in acceptSockAddr;
int iAcceptLen=sizeof(acceptSockAddr);
char szDataBuffer[1024];
int iRecvSize;
sockaddr_in tempSockAddr;
int iTempLen;
while(1)
{
fd_set fd_pre=fd;
/*
int select (
int nfds,
fd_set FAR * readfds,
fd_set FAR * writefds,
fd_set FAR * exceptfds,
const struct timeval FAR * timeout
);
*/
iResult=select(0,&fd_pre,NULL,NULL,NULL);
if(0<=iResult)
{
for(i=0;i<fd.fd_count;i++)
{
if(FD_ISSET(fd.fd_array[i],&fd_pre))
{
//如果socket是服务器,则接收连接
if(fd.fd_array[i]==ServerSocket)
{
memset(&acceptSockAddr,0,sizeof(acceptSockAddr));
//这里是接受连接
acceptSocket=accept(ServerSocket,(sockaddr *)&acceptSockAddr,&iAcceptLen);
if(acceptSocket!=INVALID_SOCKET)
{
FD_SET(acceptSocket,&fd);
printf("%s:%d has connected to server\n",inet_ntoa(acceptSockAddr.sin_addr),ntohs(acceptSockAddr.sin_port));
}
}
else//非服务器,接受数据(因为fd是读数据集)
{
memset(szDataBuffer,0,sizeof(szDataBuffer));
iRecvSize=recv(fd.fd_array[i],szDataBuffer,1024,0);
memset(&tempSockAddr,0,sizeof(tempSockAddr));
iTempLen=sizeof(tempSockAddr);
/*
The getpeername function retrieves the name of the peer connected to the socket s and
stores it in the aSOCKADDR structure identified by name.
*/
getpeername(fd.fd_array[i],(sockaddr *)&tempSockAddr,&iTempLen);
if(iRecvSize==SOCKET_ERROR)
{
closesocket(fd.fd_array[i]);
FD_CLR(fd.fd_array[i],&fd);
i--;
printf("Failed to recv data, %s:%d errorcode:%d.\n",inet_ntoa(tempSockAddr.sin_addr),ntohs(tempSockAddr.sin_port),WSAGetLastError());
continue;
}
if(iRecvSize==0)
{
printf("%s:%d has closed!\n",inet_ntoa(tempSockAddr.sin_addr),ntohs(tempSockAddr.sin_port));
closesocket(fd.fd_array[i]);
FD_CLR(fd.fd_array[i],&fd);
i--;
}
if(iRecvSize>0)
{
printf("recv %s:%d data:%s\n",inet_ntoa(tempSockAddr.sin_addr),ntohs(tempSockAddr.sin_port),szDataBuffer);
}
}
}
}
}
}
WSACleanup();
return 0;
}