简体   繁体   English

winsock服务器同时发送和接收

[英]winsock server send and receive simultaniously

I'm new to WinSock, and I'm trying something out.我是 WinSock 的新手,我正在尝试一些东西。 I have client and server programs that are communicating with each other.我有相互通信的客户端和服务器程序。 If the client types something, the server will just echo it back.如果客户端键入某些内容,服务器只会将其回显。 I want them to receive and send at the same time, so I put the client in non-blocking mode, and it works kind-of OK.我希望它们同时接收和发送,所以我将客户端置于非阻塞模式,它工作起来还不错。 But when I try to put the server in non-blocking, it crashes saying that recv() == SOCKET_ERROR .但是当我尝试将服务器置于非阻塞状态时,它会崩溃说recv() == SOCKET_ERROR

So the question is: why can the client work in non-blocking, but the server can't?那么问题来了:为什么客户端可以非阻塞工作,而服务器不能呢? How can I solve this?我该如何解决这个问题?

TCP_SERVER: TCP_SERVER:

#include <iostream>
#include <WS2tcpip.h>
#include <Windows.h>

#pragma comment (lib,"ws2_32.lib")

using namespace std;


string receive(SOCKET clientSocket, char* buf)
{
   ZeroMemory(buf, 4096);
   int bytesReceived = recv(clientSocket, buf, 4096, 0);
   string bufStr = buf;
   cout << "bytes received: " << bytesReceived << endl;
   if (bytesReceived == SOCKET_ERROR)
   {
      cerr << "error met recv() in de reciev() functie" << endl;
      exit(EXIT_FAILURE);
   }

   if (bytesReceived == 0)
   {
      cout << "client disconnected" << endl;
      exit(EXIT_FAILURE);
   }

   return bufStr;
}


 void main()
 {
  //initialize winsock
  WSADATA wsData;
  WORD ver = MAKEWORD(2, 2);

  int wsOk = WSAStartup(ver, &wsData);
  if (wsOk != 0) {
      cerr << "can't initialize winsock ABORT";
      return;
  }


  //create socket
  SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
  if (listening == INVALID_SOCKET) {
     cerr << "cant create socket ABORT" << std::endl;
  }
  //bind IP adress and port to socket
  sockaddr_in hint;
  hint.sin_family = AF_INET;
  hint.sin_port = htons(54000);
  hint.sin_addr.S_un.S_addr = INADDR_ANY; //could also inet_pton


  bind(listening, (sockaddr*)&hint, sizeof(hint));

  //tell winsock the socket is for listening
  listen(listening, SOMAXCONN);

  //wait for connection
  sockaddr_in client;
  int clientSize = sizeof(client);

  SOCKET clientSocket = accept(listening, (sockaddr*)&client, &clientSize);
  if (clientSocket == INVALID_SOCKET) {
     cerr << "somthing went wrong with client socket accept ABORT";
     exit(EXIT_FAILURE);
  }

  char host[NI_MAXHOST];      //client remote name
  char service[NI_MAXSERV];   //service (i.e port) the client is connected on

  ZeroMemory(host, NI_MAXHOST);
  ZeroMemory(service, NI_MAXSERV);

  if (getnameinfo((sockaddr*)&client, sizeof(client), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0) 
  {
     cout << host << " connected on port " << service << endl;

 }
 else {
    inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
    cout << host << " connected on port " << ntohs(client.sin_port) << endl;
  }

//close listening socket
closesocket(listening);


//non blocking socket leads to error
u_long mode = 1;  // 1 to enable non-blocking socket
ioctlsocket(clientSocket, FIONBIO, &mode);
//non blocking socket


//while loop: accept and echo message to client
char buf[4096];
string inputTxt;

while (true)
{
    inputTxt = receive(clientSocket,buf);
    send(clientSocket, buf, inputTxt.size() + 1, 0);

}

  closesocket(clientSocket);
  WSACleanup();

}

TCP_CLIENT: TCP_CLIENT:

#include <iostream>
#include <WS2tcpip.h>
#include <Windows.h>

#pragma comment (lib,"ws2_32.lib")

using namespace std;


string receive(SOCKET clientSocket, char* buf)
{
   ZeroMemory(buf, 4096);
   int bytesReceived = recv(clientSocket, buf, 4096, 0);
   string bufStr = buf;
   cout << "bytes received: " << bytesReceived << endl;
   if (bytesReceived == SOCKET_ERROR)
   {
      cerr << "error met recv() in de reciev() functie" << endl;
      exit(EXIT_FAILURE);
   }

   if (bytesReceived == 0)
   {
      cout << "client disconnected" << endl;
      exit(EXIT_FAILURE);
   }

   return bufStr;
}

void main()
{
//initialize winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);

int wsOk = WSAStartup(ver, &wsData);
if (wsOk != 0) {
    cerr << "can't initialize winsock ABORT";
    return;
}


//create socket
SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
if (listening == INVALID_SOCKET) {
    cerr << "cant create socket ABORT" << std::endl;
}
//bind IP adress and port to socket
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(54000);
hint.sin_addr.S_un.S_addr = INADDR_ANY; //could also inet_pton


bind(listening, (sockaddr*)&hint, sizeof(hint));

//tell winsock the socket is for listening
listen(listening, SOMAXCONN);

//wait for connection
sockaddr_in client;
int clientSize = sizeof(client);

SOCKET clientSocket = accept(listening, (sockaddr*)&client, &clientSize);
if (clientSocket == INVALID_SOCKET) {
    cerr << "somthing went wrong with client socket accept ABORT";
    exit(EXIT_FAILURE);
}

char host[NI_MAXHOST];      //client remote name
char service[NI_MAXSERV];   //service (i.e port) the client is connected on

ZeroMemory(host, NI_MAXHOST);
ZeroMemory(service, NI_MAXSERV);

if (getnameinfo((sockaddr*)&client, sizeof(client), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0) {
    cout << host << " connected on port " << service << endl;

}
else {
    inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
    cout << host << " connected on port " << ntohs(client.sin_port) << endl;
}

//close listening socket
closesocket(listening);

/*
//non blocking socket leads to error
u_long mode = 1;  // 1 to enable non-blocking socket
ioctlsocket(clientSocket, FIONBIO, &mode);
//non blocking socket
*/

//while loop: accept and echo message to client
char buf[4096];
string inputTxt;

while (true)
{
    inputTxt = receive(clientSocket,buf);
    send(clientSocket, buf, inputTxt.size() + 1, 0);

}

closesocket(clientSocket);
WSACleanup();

}

You are not handling the case where send() / recv() are failing due to a WSAEWOULDBLOCK error, which is NOT a fatal error.您没有处理send() / recv()由于WSAEWOULDBLOCK错误而失败的情况,这不是致命错误。 It just means there is no work to be done at that moment, try again later.这只是表示此时没有工作要做,稍后再试。

For recv() , that means there are no bytes available to read from the socket's receive buffer.对于recv() ,这意味着没有可从套接字的接收缓冲区读取的字节。 The socket will be in a readable state when there are bytes available to read from it, or the peer has performed a graceful disconnect.当有字节可供读取时,套接字将位于可读的 state 中,或者对等方已执行正常断开连接。

For send() , it means the peer's receive buffer is full and can't receive new bytes until the peer reads some bytes to clear up buffer space.对于send() ,这意味着对等方的接收缓冲区已满,并且在对等方读取一些字节以清理缓冲区空间之前无法接收新字节。 Any unsent bytes will have to be passed to send() again at a later time.任何未发送的字节都必须在以后再次传递给send() The socket will be in a writable state when new bytes can be sent to the peer, and not in a writable state when the peer's buffer is full.当新字节可以发送到对等方时,套接字将位于可写的 state 中,而当对等方的缓冲区已满时,套接字将不在可写的 state 中。

When your server accepts a client and tries to receive() from it, recv() is going to keep failing with WSAEWOULDBLOCK until the client actually sends something.当您的服务器接受客户端并尝试从它receive()时, recv()将继续失败并出现WSAEWOULDBLOCK直到客户端实际发送某些内容。

So, you need to handle WSAEWOULDBLOCK properly and retry as needed.因此,您需要正确处理WSAEWOULDBLOCK并根据需要重试。 Or better, use select() (or WSAAsyncSelect() , or WSAEventSelect() , or Overlapped I/O) to detect the socket's actual state to know when send() / recv() can be safely called without causing an WSAEWOULDBLOCK error.或者更好的是,使用select() (或WSAAsyncSelect()WSAEventSelect()或 Overlapped I/O)来检测套接字的实际 state 以了解何时可以安全地调用send() / recv()而不会导致WSAEWOULDBLOCK错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM