我参加了一个交流课程,他们给了我们客户端和服务器的c ++ tcp代码,客户端询问时间是什么,服务器做出了响应。服务器可以维护与多个客户端的多个连接。服务器包含一个名为socketState的数据结构,该数据结构包含有关特定套接字的数据,因此即使fd_set清除了其记忆后,该数据结构也将被保存:
struct SocketState
{
SOCKET id; // Socket handle
int recv; // Receiving?
int send; // Sending?
int sendSubType; // Sending sub-type
char buffer[128];
int len;
}
服务器具有60个此类型元素的数组,并具有单独的功能(通过更新相关元素的相关字段来从此数组中添加或删除套接字)以及用于接受新连接以及发送/接收消息的功能。
服务器代码为:
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
using namespace std;
#include <winsock2.h>
#include <string.h>
#include <time.h>
# include <ctime>
#include <ws2tcpip.h>
#include <assert.h>
#pragma comment(lib, "Ws2_32.lib")
struct SocketState
{
SOCKET id; // Socket handle
int recv; // Receiving?
int send; // Sending?
int sendSubType; // Sending sub-type
char buffer[128];
int len;
};
const int TIME_PORT = 27015;
const int MAX_SOCKETS = 60;
const int EMPTY = 0;
const int LISTEN = 1;
const int RECEIVE = 2;
const int IDLE = 3;
const int SEND = 4;
const int SEND_TIME = 1;
const int SEND_SECONDS = 2;
bool addSocket(SOCKET id, int what);
void removeSocket(int index);
void acceptConnection(int index);
void receiveMessage(int index);
void sendMessage(int index);
struct SocketState sockets[MAX_SOCKETS] = { 0 };
int socketsCount = 0;
void main()
{
WSAData wsaData;
if (NO_ERROR != WSAStartup(MAKEWORD(2, 2), &wsaData))
{
cout << "Time Server: Error at WSAStartup()\n";
return;
}
SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == listenSocket)
{
cout << "Time Server: Error at socket(): " << WSAGetLastError() << endl;
WSACleanup();
return;
}
sockaddr_in serverService;
serverService.sin_family = AF_INET;
serverService.sin_addr.s_addr = INADDR_ANY;
serverService.sin_port = htons(TIME_PORT);
if (SOCKET_ERROR == bind(listenSocket, (SOCKADDR*)& serverService, sizeof(serverService)))
{
cout << "Time Server: Error at bind(): " << WSAGetLastError() << endl;
closesocket(listenSocket);
WSACleanup();
return;
}
if (SOCKET_ERROR == listen(listenSocket, 5))
{
cout << "Time Server: Error at listen(): " << WSAGetLastError() << endl;
closesocket(listenSocket);
WSACleanup();
return;
}
addSocket(listenSocket, LISTEN);
while (true)
{
fd_set waitRecv;
FD_ZERO(&waitRecv);
for (int i = 0; i < MAX_SOCKETS; i++)
{
if ((sockets[i].recv == LISTEN) || (sockets[i].recv == RECEIVE))
FD_SET(sockets[i].id, &waitRecv);
}
fd_set waitSend;
FD_ZERO(&waitSend);
for (int i = 0; i < MAX_SOCKETS; i++)
{
if (sockets[i].send == SEND)
FD_SET(sockets[i].id, &waitSend);
}
struct timeval timeout;
timeout.tv_sec = 15;
timeout.tv_usec = 0;
int nfd = select(0, &waitRecv, &waitSend, NULL, NULL);
if (nfd == SOCKET_ERROR)
{
cout << "Time Server: Error at select(): " << WSAGetLastError() << endl;
WSACleanup();
return;
}
for (int i = 0; i < MAX_SOCKETS && nfd > 0; i++)
{
if (FD_ISSET(sockets[i].id, &waitRecv))
{
nfd--;
switch (sockets[i].recv)
{
case LISTEN:
acceptConnection(i);
break;
case RECEIVE:
receiveMessage(i);
break;
}
}
}
for (int i = 0; i < MAX_SOCKETS && nfd > 0; i++)
{
if (FD_ISSET(sockets[i].id, &waitSend))
{
nfd--;
switch (sockets[i].send)
{
case SEND:
sendMessage(i);
break;
}
}
}
}
// Closing connections and Winsock.
cout << "Time Server: Closing Connection.\n";
closesocket(listenSocket);
WSACleanup();
}
bool addSocket(SOCKET id, int what)
{
for (int i = 0; i < MAX_SOCKETS; i++)
{
if (sockets[i].recv == EMPTY)
{
sockets[i].id = id;
sockets[i].recv = what;
sockets[i].send = IDLE;
sockets[i].len = 0;
socketsCount++;
return (true);
}
}
return (false);
}
void removeSocket(int index)
{
sockets[index].recv = EMPTY;
sockets[index].send = EMPTY;
socketsCount--;
}
void acceptConnection(int index)
{
SOCKET id = sockets[index].id;
struct sockaddr_in from; // Address of sending partner
int fromLen = sizeof(from);
SOCKET msgSocket = accept(id, (struct sockaddr*) & from, &fromLen);
if (INVALID_SOCKET == msgSocket)
{
cout << "Time Server: Error at accept(): " << WSAGetLastError() << endl;
return;
}
cout << "Time Server: Client " << inet_ntoa(from.sin_addr) << ":" << ntohs(from.sin_port) << " is connected." << endl;
//
// Set the socket to be in non-blocking mode.
//
unsigned long flag = 1;
if (ioctlsocket(msgSocket, FIONBIO, &flag) != 0)
{
cout << "Time Server: Error at ioctlsocket(): " << WSAGetLastError() << endl;
}
if (addSocket(msgSocket, RECEIVE) == false)
{
cout << "\t\tToo many connections, dropped!\n";
closesocket(id);
}
return;
}
void receiveMessage(int index)
{
SOCKET msgSocket = sockets[index].id;
int len = sockets[index].len;
int bytesRecv = recv(msgSocket, &sockets[index].buffer[len], sizeof(sockets[index].buffer) - len, 0);
if (SOCKET_ERROR == bytesRecv)
{
cout << "Time Server: Error at recv(): " << WSAGetLastError() << endl;
closesocket(msgSocket);
removeSocket(index);
return;
}
if (bytesRecv == 0)
{
closesocket(msgSocket);
removeSocket(index);
return;
}
else
{
sockets[index].buffer[len + bytesRecv] = '\0'; //add the null-terminating to make it a string
cout << "Time Server: Recieved: " << bytesRecv << " bytes of \"" << &sockets[index].buffer[len] << "\" message.\n";
sockets[index].len += bytesRecv;
if (sockets[index].len > 0)
{
if (strncmp(sockets[index].buffer, "TimeString", 10) == 0)
{
sockets[index].send = SEND;
sockets[index].sendSubType = SEND_TIME;
memcpy(sockets[index].buffer, &sockets[index].buffer[10], sockets[index].len - 10);
sockets[index].len -= 10;
return;
}
else if (strncmp(sockets[index].buffer, "SecondsSince1970", 16) == 0)
{
sockets[index].send = SEND;
sockets[index].sendSubType = SEND_SECONDS;
memcpy(sockets[index].buffer, &sockets[index].buffer[16], sockets[index].len - 16);
sockets[index].len -= 16;
return;
}
else if (strncmp(sockets[index].buffer, "Exit", 4) == 0)
{
closesocket(msgSocket);
removeSocket(index);
return;
}
}
}
}
void sendMessage(int index)
{
int bytesSent = 0;
char sendBuff[255];
SOCKET msgSocket = sockets[index].id;
if (sockets[index].sendSubType == SEND_TIME)
{
// Answer client's request by the current time string.
// Get the current time.
time_t timer;
time(&timer);
// Parse the current time to printable string.
strcpy(sendBuff, ctime(&timer));
sendBuff[strlen(sendBuff) - 1] = 0; //to remove the new-line from the created string
}
else if (sockets[index].sendSubType == SEND_SECONDS)
{
// Answer client's request by the current time in seconds.
// Get the current time.
time_t timer;
time(&timer);
// Convert the number to string.
_itoa((int)timer, sendBuff, 10);
}
bytesSent = send(msgSocket, sendBuff, (int)strlen(sendBuff), 0);
if (SOCKET_ERROR == bytesSent)
{
cout << "Time Server: Error at send(): " << WSAGetLastError() << endl;
return;
}
cout << "Time Server: Sent: " << bytesSent << "\\" << strlen(sendBuff) << " bytes of \"" << sendBuff << "\" message.\n";
sockets[index].send = IDLE;
}
这是我的客户代码:
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
using namespace std;
#include <winsock2.h>
#include <string.h>
#include <iomanip>
#include <time.h>
# include <ctime>
#include <ws2tcpip.h>
#include <assert.h>
#pragma comment(lib, "Ws2_32.lib")
const int TIME_PORT = 27015;
void main()
{
// Initialize Winsock (Windows Sockets).
WSAData wsaData;
if (NO_ERROR != WSAStartup(MAKEWORD(2, 2), &wsaData))
{
cout << "Time Client: Error at WSAStartup()\n";
return;
}
// Client side:
// Create a socket and connect to an internet address.
SOCKET connSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == connSocket)
{
cout << "Time Client: Error at socket(): " << WSAGetLastError() << endl;
WSACleanup();
return;
}
sockaddr_in server;
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_port = htons(TIME_PORT);
if (SOCKET_ERROR == connect(connSocket, (SOCKADDR*)& server, sizeof(server)))
{
cout << "Time Client: Error at connect(): " << WSAGetLastError() << endl;
closesocket(connSocket);
WSACleanup();
return;
}
// Send and receive data.
int bytesSent = 0;
int bytesRecv = 0;
char sendBuff[255];
char recvBuff[255];
int option = 0;
while (option != 3)
{
cout << "\nPlease choose an option:\n";
cout << "1 - Get current time.\n";
cout << "2 - Get current time in seconds.\n";
cout << "3 - Exit.\n";
cout << ">> ";
cin >> option;
if (option == 1)
strcpy(sendBuff, "TimeString");
else if (option == 2)
strcpy(sendBuff, "SecondsSince1970");
else if (option == 3)
strcpy(sendBuff, "Exit");
bytesSent = send(connSocket, sendBuff, (int)strlen(sendBuff), 0);
if (SOCKET_ERROR == bytesSent)
{
cout << "Time Client: Error at send(): " << WSAGetLastError() << endl;
closesocket(connSocket);
WSACleanup();
return;
}
cout << "Time Client: Sent: " << bytesSent << "/" << strlen(sendBuff) << " bytes of \"" << sendBuff << "\" message.\n";
// Gets the server's answer for options 1 and 2.
if (option == 1 || option == 2)
{
bytesRecv = recv(connSocket, recvBuff, 255, 0);
if (SOCKET_ERROR == bytesRecv)
{
cout << "Time Client: Error at recv(): " << WSAGetLastError() << endl;
closesocket(connSocket);
WSACleanup();
return;
}
if (bytesRecv == 0)
{
cout << "Server closed the connection\n";
return;
}
recvBuff[bytesRecv] = '\0'; //add the null-terminating to make it a string
cout << "Time Client: Recieved: " << bytesRecv << " bytes of \"" << recvBuff << "\" message.\n";
}
else if (option == 3)
{
// Closing connections and Winsock.
cout << "Time Client: Closing Connection.\n";
closesocket(connSocket);
WSACleanup();
}
}
}
现在,我的主要问题是,每当我通过Windows的命令提示符,Putty或客户机的Visiual Studio代码从一个或多个客户机发送消息时,select函数都会保持阻塞状态(使用超时,但随后返回0),我真的不知道为什么。我猜这是对select的强链主义的基本理解问题,即使我从许多来源中了解了该功能。 另外,当我尝试通过cmd上的“ telnet端口号”命令连接服务器时,我只会得到黑屏。
在select文档中,它说第一个参数被忽略。但是据我所知,它代表fdset可以监视的套接字句柄数。那么,尽管如此,我如何可以访问该属性呢?或者也许我不需要,对于阻塞状态还有另一种解决方案?
如果有人可以帮助我,将不胜感激。