2013-03-11 30 views
0

这是一个聊天应用程序代码,下面我遇到了麻烦。在C++中的聊天程序返回无限消息

聊天系统的工作原理是拥有一个主服务器,所有客户端连接到该主服务器。所以,这里是主服务器的代码。

#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; 
} 

回答

2

您的服务器卡在循环中的原因是因为您没有正确使用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; 
} 

更新:鉴于你最近的更新,你仍然有你的客户端代码的问题。你甚至尝试过我给你的代码吗?下面是你最近的代码修复,但我还是建议您查看上面的代码,它解决了许多其他问题与你的原代码:

#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; 
} 
+0

谢谢你的例子。关于发送消息作为句子,我必须使用字符串。然而,那么strncpy(&sbuffer.Message,buffer.c_str(),sizeof(sbuffer.Message));会抛出一个错误?任何想法该怎么办? – 2013-03-12 01:32:25

+1

将'buffer'声明为单个'string'而不是'string []'数组,并且在'strncpy()'的第一个参数中去掉'&':'string buffer; getline(cin,buffer); strncpy(sbuffer.Message,buffer.c_str(),sizeof(sbuffer.Message));' – 2013-03-12 16:18:49

+0

我仍然有问题。我按照你所说的做了一切,getline(cin,buffer)返回一个错误,以及if语句if(recv(sConnect,buffer,sizeof(sbuffer),NULL)!= SOCKET_ERROR中的“缓冲区”。 if语句中的错误说:“不存在从”std :: string“到”char *“的合适对话函数。”我尝试使用buffer.c_str(),没有工作,返回一个错误“类型为”const char *“的参数与类型为”char *“”的参数不兼容。 getline()仍然返回相同的错误“没有重载函数的实例”getline“与参数列表相匹配 – 2013-03-12 18:15:38

3

当客户端断开摆好,recv()将返回0。当客户端断开连接异常,或者发生其他任何错误对于这个问题,recv()将返回SOCKET_ERROR,然后你可以使用WSAGetLastError()找出错误的原因。当recv()返回< = 0(除了在SOCKET_ERROR/WSAEWOULDBLOCK这不是致命错误的特定情况下)时,您需要处理这两个条件并使服务器“忘记客户端”。目前你正在处理一个套接字错误,就好像你真的从客户端收到数据一样。

测试的recv()针对零的返回值,但这不是什么上的错误recv()回报(SOCKET_ERROR实际上是-1的别名,if (-1)评估为真,没有虚假)。

+0

对不起,我是新的C++插座,我主要从这个例子开始学习它们!这是要修复错误吗? if(recv(sConnect,buffer,sizeof(sbuffer),NULL)!= SOCKET_ERROR) – 2013-03-12 00:08:02

+1

是的,应该这样做。但是考虑一下,你希望你的服务器在客户端消失之后的表现如何 - 例如,你可能不应该向它们发送更多的数据。在编写套接字代码时,请仔细阅读文档 - 您或多或少总是需要检查错误。另一个容易遗漏的问题是单次调用send()并不能保证发送所有传递给它的数据 - 你需要检查返回值并可能再次调用send()。 – svk 2013-03-12 00:12:45

+0

如果客户端*优雅*断开,'recv()'返回0,而不是'SOCKET_ERROR'。 – 2013-03-12 00:16:23