本文主要是介绍Windows C++ TCP开发(使用select函数以及设置非阻塞/Reuse属性),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
1、select官方函数说明:
语法
C++
int WSAAPI select([in] int nfds,[in, out] fd_set *readfds,[in, out] fd_set *writefds,[in, out] fd_set *exceptfds,[in] const timeval *timeout );
参数
[in] nfds
已忽略。 包含 nfds 参数只是为了与 Berkeley 套接字兼容。
[in, out] readfds
一个可选指针,指向要检查的一组套接字的可读性。
[in, out] writefds
指向要检查可写性的一组套接字的可选指针。
[in, out] exceptfds
指向要检查错误的一组套接字的可选指针。
[in] timeout
选择等待的最长时间,以 TIMEVAL 结构的形式提供。 将阻止操作的 超时 参数设置为 null 。
返回值
select 函数返回fd_set结构中已就绪并包含的套接字句柄总数;如果时间限制过期,则返回 0;如果发生错误,则返回SOCKET_ERROR。 如果返回值SOCKET_ERROR,则 WSAGetLastError 可用于检索特定的错误代码。
2、阻塞设置
unsigned long cmd = 1;
nRet = ioctlsocket(sclient, FIONBIO, &cmd);
if (SOCKET_ERROR == nRet)
{
printf("Failed to set FIONBIOsocket!\n");
WSACleanup();
return 0;
}
3、设置端口 Reuse属性
bool bReuseAddr = true;
//设置端口SO_REUSEADDR属性,端口释放后可以立即被使用。
nRet = setsockopt(sclient, SOL_SOCKET, SO_REUSEADDR, (char *)&bReuseAddr, sizeof(bReuseAddr));
if (SOCKET_ERROR == nRet)
{
printf("Failed to set resueaddr socket!\n");
WSACleanup();
return 0;
}
什么是resuse属性:端口复用最常用的用途应该是防止服务器重启时之前绑定的端口还未释放或者程序突然退出而系统没有释放端口。这种情况下如果设定了端口复用,则新启动的服务器进程可以直接绑定端口。如果没有设定端口复用,绑定会失败,提示ADDR已经在使用中,必须等超时时间大概是2分钟。
4、TCP代码+select
4.1 TCP 服务端代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <WINSOCK2.H>#pragma comment(lib,"ws2_32.lib")
#define INT_SERVER_PORT 8888
#define STR_SERVER_IP "127.0.0.1"
#define INT_DATABUFFER_SIZE 100void main(void)
{
//与WSACleanup函数配对使用
WORD dwVersion = MAKEWORD(2, 2);
WSAData wsaData;
WSAStartup(WINSOCK_VERSION, &wsaData);//创建套接字
SOCKET sockServer = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == sockServer)
{
printf("Failed to create socket!\n");
WSACleanup();
return;
}//本地地址初始化
sockaddr_in addrServer;
memset(&addrServer, 0, sizeof(sockaddr_in));
addrServer.sin_family = AF_INET;
addrServer.sin_port = htons(INT_SERVER_PORT);
addrServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY);int nRet = 0;
bool bReuseAddr = true;
//设置端口SO_REUSEADDR属性,端口释放后可以立即被使用。
nRet = setsockopt(sockServer, SOL_SOCKET, SO_REUSEADDR, (char *)&bReuseAddr, sizeof(bReuseAddr));
if (SOCKET_ERROR == nRet)
{
printf("Failed to set resueaddr socket!\n");
WSACleanup();
return;
}//设置端口非阻塞
unsigned long cmd = 1;
nRet = ioctlsocket(sockServer,FIONBIO,&cmd);
if (SOCKET_ERROR == nRet)
{
printf("Failed to set resueaddr socket!\n");
WSACleanup();
return;
}nRet = bind(sockServer, (sockaddr *)&addrServer, sizeof(addrServer));
if (SOCKET_ERROR == nRet)
{
printf("Failed to bind address!\n");
WSACleanup();
return;
}if (0 != listen(sockServer, 5))
{
printf("Failed to listen client!\n");
WSACleanup();
return;
}UINT i = 0;
fd_set fd;
FD_ZERO(&fd);
FD_SET(sockServer, &fd);/*
timeval tm;
tm.tv_sec = 0;
tm.tv_usec = 1000;
*/
while (1)
{
fd_set fdOld = fd;
nRet = select(0, &fdOld, NULL, NULL,/*&tm*/NULL);
if (0 <= nRet)
{
for (i = 0; i < fd.fd_count; i++)
{
if (FD_ISSET(fd.fd_array[i], &fdOld))
{
if (fd.fd_array[i] == sockServer)//接收到新的连接
{
SOCKET sockAccept;
sockaddr_in addrAccept;
int iAcceptLen = sizeof(addrAccept);
memset(&addrAccept, 0, sizeof(addrAccept));
sockAccept = accept(sockServer, (sockaddr *)&addrAccept, &iAcceptLen);
if (INVALID_SOCKET != sockAccept)
{
send(sockAccept, "Hello Client", strlen("Hello Client"), 0);
FD_SET(sockAccept, &fd);
printf("%s:%d has connected server!\n", inet_ntoa(addrAccept.sin_addr),
ntohs(addrAccept.sin_port));
}
}
else //非服务器,接收数据(因为fd是读数据集)
{
char szDataBuff[INT_DATABUFFER_SIZE];
int iRecvSize;
memset(szDataBuff, 0, INT_DATABUFFER_SIZE);
iRecvSize = recv(fd.fd_array[i], szDataBuff, INT_DATABUFFER_SIZE, 0);
if (SOCKET_ERROR == iRecvSize)
{
closesocket(fd.fd_array[i]);
FD_CLR(fd.fd_array[i], &fd);
i--;
printf("Failed to recv data ,or Client has disconnected, errorcode:%d.\n", WSAGetLastError());
continue;
}if (0 == iRecvSize)
{
//客户socket关闭
closesocket(fd.fd_array[i]);
FD_CLR(fd.fd_array[i], &fd);
i--;
}if (0 < iRecvSize)
{
//打印接收的数据
printf("recv data:%s\n",szDataBuff);
}
}
}
}
}
else if (SOCKET_ERROR == nRet)
{
printf("Faild to select sockt in server! Error Code[%d]\n", WSAGetLastError());
Sleep(100);
}
}
WSACleanup();
}
4.2 TCP客户端代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <WINSOCK2.H>
using namespace std;
#pragma comment(lib, "ws2_32.lib")int main()
{
//与WSACleanup函数配对使用
WORD sockVersion = MAKEWORD(2, 2);
WSADATA data;
if (WSAStartup(sockVersion, &data) != 0)
{
return 0;
}//创建客户端套接字
SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sclient == INVALID_SOCKET)
{
printf("invalid socket!");
return 0;
}//初始化SOCKET结构体
sockaddr_in serAddr;
serAddr.sin_family = AF_INET;
serAddr.sin_port = htons(8888);
serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
{ //连接失败
printf("connect error !");
closesocket(sclient);
return 0;
}int nRet = 0;
bool bReuseAddr = true;
//设置端口SO_REUSEADDR属性,端口释放后可以立即被使用。
nRet = setsockopt(sclient, SOL_SOCKET, SO_REUSEADDR, (char *)&bReuseAddr, sizeof(bReuseAddr));
if (SOCKET_ERROR == nRet)
{
printf("Failed to set resueaddr socket!\n");
WSACleanup();
return 0;
}//设置端口非阻塞
unsigned long cmd = 1;
nRet = ioctlsocket(sclient, FIONBIO, &cmd);
if (SOCKET_ERROR == nRet)
{
printf("Failed to set resueaddr socket!\n");
WSACleanup();
return 0;
}//发送数据
send(sclient, "Hello Server", strlen("Hello Server"), 0);//接收数据
while (true)
{
fd_set fd;
FD_ZERO(&fd);
FD_SET(sclient, &fd);
nRet = select(0, &fd, NULL, NULL,NULL);
if (0 <= nRet)
{
char recData[255];
int ret = recv(sclient, recData, 255, 0);
if (ret > 0) {
recData[ret] = 0x00;
printf("Recv :%s\n",recData);
}
else
{
printf("Socket Error or Socket has closed\n");
break;
}
}
else if (nRet == SOCKET_ERROR)
{
printf("Faild to select sockt in server! Error Code[%d]\n", WSAGetLastError());
break;
}
}
closesocket(sclient);
WSACleanup();
return 0;
}
这篇关于Windows C++ TCP开发(使用select函数以及设置非阻塞/Reuse属性)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!