参考图书:网络与通信程序设计
使用VC6.0学习,后续使用Visual Studio 其他版本;
//////////////////////////////////////////////////////
// select.cpp
#include "../common/initsock.h"
#include <stdio.h>
CInitSock initSock; // 初始化Winsock库
int main()
{
USHORT nPort = 4567; // 此服务器监听的端口号
// 创建监听套节字
SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(nPort);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
// 绑定套节字到地址、端口
if (::bind(sListen, (sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR)
{
printf("Failed bind() \n");
return -1;
}
// 进入监听模式
::listen(sListen, 5);
// select模型处理过程
// 1)初始化一个套接字集合fdSocket,添加监听套接字句柄到这个集合
fd_set fdSocket;
FD_ZERO(&fdSocket); // 初始化集合fd_set
FD_SET(sListen, &fdSocket); // 添加套接字到集合
while (TRUE)
{
// 2)将fdSocket集合的一个拷贝fdRead传递给select函数,
// 当有事件发生时,select函数移除fdRead集合中没有未决I/O操作的套节字句柄,然后返回。
// 当select调用完成后在确定socket是否依然还在readfds集合中,如果还在,说明socket可读了;
fd_set fdRead = fdSocket;
int nRet = ::select(0, &fdRead, NULL, NULL, NULL);
if (nRet > 0)
{
// 3)通过将原来fdSocket集合与select处理过的fdRead集合比较,
// 确定都有哪些套节字有未决I/O,并进一步处理这些I/O。
for(int i = 0; i < (int)fdSocket.fd_count; i++)
{
if (FD_ISSET(fdSocket.fd_array[i], &fdRead))
{
if (fdSocket.fd_array[i] == sListen) // (1)监听套节字接收到新连接
{
if (fdSocket.fd_count < FD_SETSIZE)
{
sockaddr_in addrRemote;
int nAddrLen = sizeof(addrRemote);
SOCKET sNew = ::accept(sListen, (SOCKADDR *)&addrRemote, &nAddrLen);
FD_SET(sNew, &fdSocket);
printf("接受到连接(%s) \n", ::inet_ntoa(addrRemote.sin_addr));
}
else
{
printf("Too much connections! \n");
continue;
}
}
else
{
char szText[256];
int nRecv = ::recv(fdSocket.fd_array[i], szText, strlen(szText), 0);
if (nRecv > 0) // (2)可读
{
szText[nRecv] = '\0';
printf("接受到数据:%s\n", szText);
}
else // (3)连接关闭、重启或者中断
{
::closesocket(fdSocket.fd_array[i]);
FD_CLR(fdSocket.fd_array[i], &fdSocket);
}
}
}
}
}
else
{
printf("Failed select() \n");
break;
}
}
return 0;
}
客户端测试程序:
暂时不会开线程连接;
//////////////////////////////////////////////////////
// TCPClient.cpp
#include "../common/initsock.h"
#include <stdio.h>
CInitSock initSock; // 初始化Winsock库
int main()
{
// 创建套接字
SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET)
{
printf("Failed socket() \n");
return 0;
}
// 也可以在这里调用bind函数绑定一个本地地址
// 否则系统将会自动安排
// 填写远程地址信息
sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(4567);
// 注意,这里要填写服务器程序(TCPServer程序)所在机器的IP地址
// 如果你的计算机没有联网,直接使用127.0.0.1即可
servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
if (::connect(s, (sockaddr *) &servAddr, sizeof(servAddr)) == -1)
{
printf("Failed connect() \n");
return 0;
}
// 发送数据
char szText[] = "TCP Server Demo! \r\n";
::send(s, szText, strlen(szText), 0);
/*
// 接受数据
char buff[256];
int nRecv = ::recv(s, buff, 256, 0);
if (nRecv > 0)
{
buff[nRecv] = '\0';
printf(" 接收到数据:%s", buff);
}
*/
// 关闭套接字
::closesocket(s);
return 0;
}
///////////////////////////////////////////
// initsock.h
#include <winsock2.h>
#pragma comment(lib, "WS2_32") // 链接到WS2_32.lib
class CInitSock
{
public:
CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
{ // 初始化WS_32.dll
WSADATA wsaData;
WORD sockVersion = MAKEWORD(minorVer, majorVer);
if(::WSAStartup(sockVersion, &wsaData) != 0)
{
exit(0);
}
}
~CInitSock()
{
::WSACleanup();
}
};