0

所以我正在尝试创建一个迷你聊天应用程序,只是为了进入并理解套接字编程和关于线程的一两件事,我正在使用 c++ Mfc 进行操作,我可以在客户端和服务器之间建立连接(已验证它与netstat)但至于send和recv函数,我不太明白它应该如何完成,我从客户端应用程序发送一条消息,但服务器似乎没有收到它

服务器源代码:

int RcvThread();
SOCKET s;
void CChat_ServerDlg::OnBnClickedButton2()
{

WSADATA w;

int error = WSAStartup ( 0x0202,&w);
if(error)
{
    OnCancel();
}
 if (w.wVersion != 0x0202) 
{
    WSACleanup ();
    OnCancel();
}
SOCKADDR_IN addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(DEFAULT_PORT);
addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
s = socket ( AF_INET, SOCK_STREAM, IPPROTO_TCP);
     if (s == INVALID_SOCKET)
{
    OnCancel();
}
 if (bind(s, (LPSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR)
{

    OnCancel();
}
listen (s, SOMAXCONN);
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) RcvThread, NULL, NULL, NULL);

int buffsize = 1024;
char msg[1024] = "a";
int marker;
}

    int RcvThread()
    {
char sbuffer[256];

char buffer[sizeof(sbuffer)] = {0};

for(;; )
{

    if(recv(s, buffer, sizeof(sbuffer), NULL) > 0)
    {
        memcpy(&sbuffer, buffer, sizeof(sbuffer));
        MessageBox(hnd,sbuffer,"message",NULL);
    }
}

return 0;
    }

客户源代码:

SOCKET s;
void CChat_ClientDlg::OnBnClickedOk()
{

WSADATA wsadata;

int error = WSAStartup(0x0202,&wsadata);

 if (error)
{
    MessageBox("Error","ERRR");

    OnCancel();
}


if (wsadata.wVersion != 0x0202) 
{
    WSACleanup ();
    OnCancel();
}

SOCKADDR_IN target;

target.sin_family = AF_INET;
target.sin_port = htons(3124);
target.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

s = socket ( AF_INET, SOCK_STREAM, IPPROTO_TCP);

if (s == INVALID_SOCKET)
{
    OnCancel();
}

   if (connect(s, (SOCKADDR *)&target, sizeof(target)) == SOCKET_ERROR)
{
    OnCancel();
}
}

按钮发送消息:

void CChat_ClientDlg::OnBnClickedButton2()
{
char* Msg = new char[256];
Msg = "abdouabdouabdou";
send(s,Msg,256,NULL);
}
4

2 回答 2

2

您缺少套接字的基本概念。侦听套接字(代码中的 s)用于接受连接请求。它不用于发送/接收数据。您必须为此创建另一个套接字。您需要对文档和示例进行更多研究。一个来源是:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms738545(v=vs.85).aspx

另外,您的线程不正确。如果您使用 CreateThread,则线程函数必须匹配为 CreateThread 指定的签名。但在 MFC 应用程序中,您应该使用 AfxBeginThread 而不是 CreateThread。

于 2013-10-26T13:26:32.630 回答
1

首先,相信send()和recv()会收发是一种常见的误解。如果你使用 TCP(你应该为一个聊天程序) recv() 可以返回 0 和你指定的缓冲区限制之间的任意数量的字节。因此,如果您发送 256 个字节,您的 recv() 函数可以将其拆分为两个、三个或更多消息片段,或者它可以返回第一条消息的结尾和第二条消息的开头。经常做的是消息的第一个 n 字节(n 取决于您的最大消息大小)标记消息的长度。由于您将协议限制为 256 字节的消息长度,因此 1 字节就足够了。在接收到第一个字节后,分配一个与您的消息一样大的消息缓冲区,并将 recv() 放入一个......好吧......接收循环中,直到它接收到整个消息。您需要在接收缓冲区等中调整一些偏移量。

其次,您不太了解 bind()/listen()/accept() 系统是如何工作的。listen() 函数将套接字设置为正在侦听新客户端的被动模式。accept() 最终建立与新客户端的连接并返回一个的套接字,然后用于与客户端通信。原来的(监听)socket 继续监听新的客户端。

第三,您的发送函数处理您的字符缓冲区不正确。关于我的建议,我对其进行了一些更正(消息的第一个字节标记了以下消息的长度):

void CChat_ClientDlg::OnBnClickedButton2()
{
  std::string myMessage = "abdouabdouabdou";
  unsigned char buffer[256];
  buffer[0] = (unsigned char)myMessage.length();
  memcpy(buffer+1, myMessage.c_str(), std::min(myMessage.length(), 255));
  send(s, buffer, 256, NULL);
}

即使该方法也不正确,因为 send() 返回发送的字节数可能小于我的缓冲区大小,尽管对于如此小的消息来说这种情况很少见。

于 2013-10-26T13:37:46.420 回答