简体   繁体   English

C++ Socket 编程:Accept 和 Recv 方法不会阻塞进程

[英]C++ Socket Programming : Accept and Recv method do not block the process

I've been creating a socket programming code.我一直在创建套接字编程代码。

and I implemented server-side program as follow:我实现了服务器端程序如下:

#include "Common.h"
#include "EP_Test4.h"

int main()
{
    printf("Start EP3 \n");

    while (1){
        EP_Test4 et;
    }

    return 0;
}

this is a main code.这是一个主要代码。 and as you can see a code line is in while-statement.正如您所看到的,代码行在 while 语句中。 in there, a class is called, the constructor in the class is also called, then the "initEntryPoint()"method is called .在那里,一个类被调用,类中的构造函数也被调用,然后“initEntryPoint()”方法被调用。

EP_Test4::EP_Test4(){

    initEntryPoint();
}

in the initEntryPoint code, there are an initiation of socket method (InitCtrlSocket), a receiving data method and closing socket method.在initEntryPoint代码中,有发起socket方法(InitCtrlSocket)、接收数据方法和关闭socket方法。

void EP_Test4::initEntryPoint()
{   
    printf("[Waiting Restart Signal] \n");
    CSocket cCtrlEpSock;
    cCtrlEpSock.InitCtrlSocket();
    cCtrlEpSock.RecvRestartEPMsg();
    cCtrlEpSock.CloseCtrlSocket();
}

And those method are implemented like as follows这些方法的实现如下

void CSocket::InitCtrlSocket(){

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        printf("error\r\n");
    }

    if ((EpCtrlServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))<0)           
    {

        perror("socket error : ");
        exit(1);
    }

    memset(&server_addr, 0, sizeof(server_addr));

    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(6870);

    if (bind(EpCtrlServerSocket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        perror("bind error: ");
        exit(1);
    }

    if (listen(EpCtrlServerSocket, 5)<0)
    {
        perror("listen error : ");
        exit(1);
    }

    EpCtrlClientSocket = accept(EpCtrlServerSocket, (struct sockaddr *)&client_addr, &clen);
}

void CSocket::RecvRestartEPMsg(){
    char arrRecvCompleteMsg[50];
    memset(&arrRecvCompleteMsg, 0, sizeof(arrRecvCompleteMsg));

    int data_len = recv(EpCtrlClientSocket, (char*)&arrRecvCompleteMsg, sizeof(arrRecvCompleteMsg), 0);
    cout << "RECV CTRL MSG : " << arrRecvCompleteMsg << endl;
}

void CSocket::CloseCtrlSocket(){

    closesocket(EpCtrlServerSocket);
    closesocket(EpCtrlClientSocket);
    WSACleanup();
}

But when the program is executed, the accept method and the recv method are not waiting.但是当程序执行时,accept方法和recv方法都没有等待。 so printed messages are repeated like below image.因此打印的消息如下图所示重复。

sometimes works well(waiting at the blocking method), sometimes not.有时效果很好(等待阻塞方法),有时则不然。 I don't understand why this happen.我不明白为什么会这样。 As I know, accept method and recv method are the "blocking" method.据我所知,accept 方法和 recv 方法是“阻塞”方法。 But why those methods do sometimes block, sometimes not?但是为什么这些方法有时会阻塞,有时不会?

程序执行时

You have not shown enough code to diagnose your problem.您没有显示足够的代码来诊断您的问题。 And you are not doing any error handling on accept() or recv() .并且您没有对accept()recv()进行任何错误处理。 accept() is likely failing and you then passing an invalid socket to recv() . accept()可能失败,然后您将无效的套接字传递给recv()

I am going to go out on a limb and guess that your clen variable is uninitialized.我要clen猜测您的clen变量未初始化。 If so, it will have a random value, causing accept() to fail if clen happens to be less than sizeof(client_addr) , and succeed if clen happens to be greater than or equal to sizeof(client_addr) .如果是的话,它会产生一个随机值,导致accept()来如果失败clen恰好是小于sizeof(client_addr)并取得成功,如果clen恰好是大于或等于sizeof(client_addr)

The addrlen parameter of accept() is an IN/OUT parameter: accept()addrlen参数是一个IN/OUT参数:

addrlen [in, out] addrlen [输入,输出]
An optional pointer to an integer that contains the length of structure pointed to by the addr parameter.一个指向整数的可选指针,该整数包含由 addr 参数指向的结构的长度。

... ...

The integer referred to by addrlen initially contains the amount of space pointed to by addr. addrlen 引用的整数最初包含 addr 指向的空间量。 On return it will contain the actual length in bytes of the address returned.返回时,它将包含返回地址的实际长度(以字节为单位)。

... ...

The parameter addr is a result parameter that is filled in with the address of the connecting entity, as known to the communications layer.参数addr是一个结果参数,填充有连接实体的地址,如通信层所知。 The exact format of the addr parameter is determined by the address family in which the communication is occurring. addr 参数的确切格式由发生通信的地址族决定。 The addrlen is a value-result parameter; addrlen 是一个 value-result 参数; it should initially contain the amount of space pointed to by addr;它最初应该包含由 addr 指向的空间量; on return it will contain the actual length (in bytes) of the address returned.返回时,它将包含返回地址的实际长度(以字节为单位)。

If you don't supply the input size correctly, accept() will fail:如果您没有正确提供输入大小, accept()将失败:

WSAEFAULT WSAEFAULT
The addrlen parameter is too small or addr is not a valid part of the user address space. addrlen 参数太小或 addr 不是用户地址空间的有效部分。

Try this instead:试试这个:

clen = sizeof(client_addr); // <-- add this
EpCtrlClientSocket = accept(EpCtrlServerSocket, (struct sockaddr *)&client_addr, &clen);
if (EpCtrlClientSocket < 0) // <-- add this
{
    perror("accept error : ");
    exit(1);
}

void CSocket::RecvRestartEPMsg(){
    char arrRecvCompleteMsg[50];    
    int data_len = recv(EpCtrlClientSocket, arrRecvCompleteMsg, sizeof(arrRecvCompleteMsg), 0);
    if (data_len > 0) // <-- add this
    {
        cout << "RECV CTRL MSG : ";
        cout.write(arrRecvCompleteMsg, data_len);
        cout << endl;
    }
    else
        cout << "RECV CTRL ERR : " << endl;
}

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

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