下面是一个我遇到麻烦的聊天应用程序代码。
聊天系统的工作原理是有一个主服务器,所有客户端都连接到主服务器。下面是主服务器的代码。
#pragma comment(lib, "Ws2_32.lib")
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
using namespace std;
SOCKADDR_IN addr;
SOCKET sListen;
SOCKET sConnect;
SOCKET* Connections;
int addrlen = sizeof(addr);
int ConCounter = 0;
struct Buffer
{
int ID;
char Message[256];
};
int ServerThread(int ID)
{
Buffer sbuffer;
char* Recv = new char[256];
ZeroMemory(Recv, 256);
char* Send = new char[sizeof(Buffer)];
ZeroMemory(Send, sizeof(Buffer));
for(;; Sleep(10))
{
if(recv(Connections[ID], Recv, 256, NULL))
{
sbuffer.ID = ID;
memcpy(sbuffer.Message, Recv, 256);
memcpy(Send, &sbuffer, sizeof(Buffer));
for(int a = 0; a != ConCounter; a++)
{
if(Connections[a] == Connections[ID])
{
}
else
{
send(Connections[a], Send, sizeof(Buffer), NULL);
}
}
ZeroMemory(Recv, 256);
}
}
return 0;
}
int InitWinSock()
{
int RetVal = 0;
WSAData wsaData;
WORD DllVersion = MAKEWORD(2,1);
RetVal = WSAStartup(DllVersion, &wsaData);
return RetVal;
}
int main()
{
int RetVal = 0;
RetVal = InitWinSock();
if(RetVal != 0)
{
MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
Connections = (SOCKET*)calloc(64, sizeof(SOCKET));
sListen = socket(AF_INET, SOCK_STREAM, NULL);
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1234);
addr.sin_family = AF_INET;
bind(sListen, (SOCKADDR*)&addr, sizeof(addr));
listen(sListen, 64);
for(;; Sleep(50))
{
if(sConnect = accept(sListen, (SOCKADDR*)&addr, &addrlen))
{
Connections[ConCounter] = sConnect;
char* ID = new char[64];
ZeroMemory(ID, sizeof(ID));
itoa(ConCounter, ID, 10);
send(Connections[ConCounter], ID, sizeof(ID), NULL);
ConCounter = ConCounter + 1;
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) ServerThread, (LPVOID)(ConCounter - 1), NULL, NULL);
}
}
return 0;
}
下面是客户端聊天的源代码:
#pragma comment(lib, "Ws2_32.lib")
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
using namespace std;
SOCKADDR_IN addr;
SOCKET sConnect;
// For this we need to send two information at one time:
// 1. The main message
// 2. The ID
// To send more than one information I will use a struct
struct Buffer
{
int ID;
char Message[256];
};
int ClientThread()
{
Buffer sbuffer;
char buffer[sizeof(sbuffer)] = {0};
for(;; Sleep(10))
{
// The server will send a struct to the client
// containing message and ID
// But send only accepts a char as buffer parameter
// so here we need to recv a char buffer and then
// we copy the content of this buffer to our struct
if(recv(sConnect, buffer, sizeof(sbuffer), NULL))
{
memcpy(&sbuffer, buffer, sizeof(sbuffer));
cout << "<Client " << sbuffer.ID << ":> " << sbuffer.Message <<endl;
}
}
return 0;
}
int main()
{
system("cls");
int RetVal = 0;
WSAData wsaData;
WORD DllVersion = MAKEWORD(2,1);
RetVal = WSAStartup(DllVersion, &wsaData);
if(RetVal != 0)
{
MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1234);
addr.sin_family = AF_INET;
cout << "Connect to Masterserver? [ENTER]" <<endl;
getchar();
RetVal = connect(sConnect, (SOCKADDR*)&addr, sizeof(addr));
if(RetVal != 0)
{
MessageBoxA(NULL, "Could not connect to server", "Error", MB_OK | MB_ICONERROR);
main();
}
else
{
int ID;
char* cID = new char[64];
ZeroMemory(cID, 64);
recv(sConnect, cID, 64, NULL);
ID = atoi(cID);
cout << "Connected" <<endl;
cout << "You are Client No: " << ID <<endl;
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) ClientThread, NULL, NULL, NULL);
for(;; Sleep(10))
{
char* buffer = new char[256];
ZeroMemory(buffer, 256);
cin >> buffer;
getchar();
send(sConnect, buffer, 256, NULL);
}
}
return 0;
}
现在,一切都工作得很好,除了当你连接例如两个客户端(运行应用程序两次),并关闭其中一个客户端,关闭应用程序垃圾邮件聊天无限的消息,永远不会停止!有什么办法吗?
我想问别人,如果可能的话,帮助我评论源代码!
更新代码:
#pragma comment(lib, "Ws2_32.lib")
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
#include <string>
using namespace std;
SOCKADDR_IN addr;
SOCKET sConnect;
struct Buffer
{
int ID;
char Message[256];
};
int ClientThread()
{
Buffer sbuffer;
string buffer;
//char buffer[sizeof(sbuffer)] = {0};
for(;; Sleep(10))
{
if(recv(sConnect, buffer.c_str(), sizeof(sbuffer), NULL)!=SOCKET_ERROR)
{
strncpy(sbuffer.Message, buffer.c_str(), sizeof(sbuffer.Message));
cout << "<Client " << sbuffer.ID << ":> " << sbuffer.Message <<endl;
}
}
return 0;
}
int main()
{
system("cls");
int RetVal = 0;
WSAData wsaData;
WORD DllVersion = MAKEWORD(2,1);
RetVal = WSAStartup(DllVersion, &wsaData);
if(RetVal != 0)
{
MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1234);
addr.sin_family = AF_INET;
cout << "Connect to Masterserver? [ENTER]" <<endl;
getchar();
RetVal = connect(sConnect, (SOCKADDR*)&addr, sizeof(addr));
if(RetVal != 0)
{
MessageBoxA(NULL, "Could not connect to server", "Error", MB_OK | MB_ICONERROR);
main();
}
else
{
int ID;
char* cID = new char[64];
ZeroMemory(cID, 64);
recv(sConnect, cID, 64, NULL);
ID = atoi(cID);
cout << "Connected" <<endl;
cout << "You are Client No: " << ID <<endl;
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) ClientThread, NULL, NULL, NULL);
for(;; Sleep(10))
{
char* buffer = new char[256];
ZeroMemory(buffer, 256);
getline(cin,buffer);
//cin >> buffer;
getchar();
send(sConnect, buffer, 256, NULL);
}
}
return 0;
}
当客户端正常断开连接时,recv()
将返回0。当客户端异常断开连接,或发生任何其他错误时,recv()
将返回SOCKET_ERROR
,然后您可以使用WSAGetLastError()
来找出错误的原因。您需要处理这两种情况,并使服务器在recv()
返回<= 0时"忘记客户端"(SOCKET_ERROR
/WSAEWOULDBLOCK
的特定情况除外,这不是致命错误)。当前你正在处理套接字错误,就好像你真的从客户端收到了数据。
您正在测试recv()
的返回值是否为零,但这不是recv()
在错误时返回的值(SOCKET_ERROR
实际上是-1的别名,if (-1)
的计算结果为真,而不是假)。
服务器陷入循环的原因是因为您没有正确使用recv()
的返回值。
你的代码还有其他问题(对于初学者来说,滥用CreateThread()
)。
试试这个:
#pragma comment(lib, "Ws2_32.lib")
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
using namespace std;
SOCKADDR_IN addr;
int addrlen;
SOCKET sListen;
SOCKET sConnect;
SOCKET Connections[64];
struct Buffer
{
int ID;
char Message[256];
};
bool doSend(SOCKET s, void *data, int datalen)
{
char pdata = (char*) data;
while (datalen > 0)
{
int numSent = send(s, pdata, datalen, NULL);
if (numSent < 1)
return false;
pdata += numSent;
datalen -= numSent;
}
return true;
}
DWORD WINAPI ServerThread(LPVOID lpParam)
{
int ID = (int) lpParam;
SOCKET sThisClient = Connections[ConID];
char cID[64];
ZeroMemory(cID, sizeof(cID));
itoa(ID, cID, 10);
if (doSend(sThisClient, cID, sizeof(cID)))
{
Buffer sbuffer;
sbuffer.ID = ID;
ZeroMemory(sbuffer.Message, sizeof(sbuffer.Message));
for (;; Sleep(10))
{
int numRecv = recv(sThisClient, sbuffer.Message, sizeof(sbuffer.Message), NULL);
if (numRecv < 1)
break;
for (int a = 0; a < 64; a++)
{
SOCKET sOtherClient = Connections[a];
if ((sOtherClient != INVALID_SOCKET) && (sOtherClient != sClient))
doSend(sOtherClient, &sbuffer, sizeof(Buffer));
}
}
ZeroMemory(sbuffer.Message, sizeof(sbuffer.Message));
}
closesocket(Connections[ID]);
Connections[ID] = INVALID_SOCKET;
return 0;
}
int main()
{
for (int i = 0; i < 64; ++i)
Connections[i] = INVALID_SOCKET;
WSAData wsaData;
int RetVal = WSAStartup(MAKEWORD(2,1), &wsaData);
if (RetVal != 0)
{
MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
sListen = socket(AF_INET, SOCK_STREAM, NULL);
if (sListen == INVALID_SOCKET)
{
MessageBoxA(NULL, "Socket create failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
if (sConnect == INVALID_SOCKET)
{
MessageBoxA(NULL, "Socket create failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1234);
addr.sin_family = AF_INET;
if (bind(sListen, (SOCKADDR*)&addr, sizeof(addr)) != 0)
{
MessageBoxA(NULL, "bind failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
if (listen(sListen, 64) != 0)
{
MessageBoxA(NULL, "listen failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
for(;; Sleep(50))
{
addrlen = sizeof(addr);
sConnect = accept(sListen, (SOCKADDR*)&addr, &addrlen);
if (sConnect != INVALID_SOCKET)
{
int ConID = -1;
for (int i = 0; i < 64; ++i)
{
if (Connections[i] == INVALID_SOCKET);
{
ConID = i;
break;
}
}
if (ConID == -1)
{
closesocket(sConnect);
continue;
}
Connections[ConID] = sConnect;
HANDLE hThread = CreateThread(NULL, NULL, &ServerThread, (LPVOID)ConID, NULL, NULL);
if (!hThread)
{
closesocket(sConnect);
Connections[ConID] = INVALID_SOCKET;
continue;
}
CloseHandle(hThread);
}
}
return 0;
}
.
#pragma comment(lib, "Ws2_32.lib")
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
using namespace std;
SOCKADDR_IN addr;
SOCKET sConnect;
struct Buffer
{
int ID;
char Message[256];
};
bool doRecv(SOCKET s, void *data, int datalen)
{
char pdata = (char*) data;
while (datalen > 0)
{
int numRecv = recv(s, pdata, datalen, NULL);
if (numRecv < 1)
return false;
pdata += numRecv;
datalen -= numRecv;
}
return true;
}
DWORD WINAPI ClientThread(LPVOID lpParam)
{
Buffer sbuffer;
for(;; Sleep(10))
{
if (!doRecv(sConnect, &sbuffer, sizeof(sbuffer)))
break;
cout << "<Client " << sbuffer.ID << ":> " << sbuffer.Message << endl;
}
return 0;
}
int main()
{
system("cls");
WSAData wsaData;
int RetVal = WSAStartup(MAKEWORD(2,1), &wsaData);
if (RetVal != 0)
{
MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
if (sConnect == INVALID_SOCKET)
{
MessageBoxA(NULL, "Socket create failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1234);
addr.sin_family = AF_INET;
do
{
cout << "Connect to Masterserver? [ENTER]" << endl;
getchar();
RetVal = connect(sConnect, (SOCKADDR*)&addr, sizeof(addr));
if (RetVal == 0)
break;
MessageBoxA(NULL, "Could not connect to server", "Error", MB_OK | MB_ICONERROR);
}
while (true);
char cID[64];
ZeroMemory(cID, 64);
if (!doRecv(sConnect, cID, 64))
exit(1);
int ID = atoi(cID);
cout << "Connected" << endl;
cout << "You are Client ID: " << ID << endl;
if (!CreateThread(NULL, NULL, &ClientThread, NULL, NULL, NULL))
exit(1);
for(;; Sleep(10))
{
string buffer;
getline(cin, buffer);
doSend(sConnect, buffer.c_str(), buffer.length());
}
return 0;
}
Update:给定你最近的更新,你的客户端代码仍然有问题。你试过我上面给你的密码吗?下面是对你最近的代码的修复,尽管我仍然建议你检查上面的代码,它解决了你的原始代码中的许多其他问题:
#pragma comment(lib, "Ws2_32.lib")
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
#include <string>
using namespace std;
SOCKADDR_IN addr;
SOCKET sConnect;
struct Buffer
{
int ID;
char Message[256];
};
int ClientThread()
{
Buffer sbuffer;
char buffer[sizeof(sbuffer)];
for(;; Sleep(10))
{
int numRead = recv(sConnect, &buffer, sizeof(buffer), NULL);
if (numRead < 1) break;
memcpy(&sbuffer, buffer, numRead);
cout << "<Client " << sbuffer.ID << ":> " << sbuffer.Message << endl;
}
return 0;
}
int main()
{
system("cls");
int RetVal = 0;
WSAData wsaData;
WORD DllVersion = MAKEWORD(2,1);
RetVal = WSAStartup(DllVersion, &wsaData);
if (RetVal != 0)
{
MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1234);
addr.sin_family = AF_INET;
do
{
cout << "Connect to Masterserver? [ENTER]" <<endl;
getchar();
RetVal = connect(sConnect, (SOCKADDR*)&addr, sizeof(addr));
if (RetVal == 0) break;
MessageBoxA(NULL, "Could not connect to server", "Error", MB_OK | MB_ICONERROR);
}
while (true);
char cID[64];
ZeroMemory(cID, 64);
recv(sConnect, cID, 64, NULL);
int ID = atoi(cID);
cout << "Connected" << endl;
cout << "You are Client No: " << ID << endl;
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) ClientThread, NULL, NULL, NULL);
for(;; Sleep(10))
{
string buffer;
getline(cin, buffer);
if (send(sConnect, buffer.c_str(), buffer.length(), NULL) < 1)
exit(1);
}
return 0;
}