几次操作后读取返回 0

And*_*ria 0 c++ linux posix pipe

我在客户端和服务器之间有一个 TCP 多线程通信。我不知道为什么第三次使用 read 函数时它只给出 0。

  1. 客户端代码
while(1)
    {
        /* citirea raspunsului dat de server
              (apel blocant pina cind serverul raspunde) */
        if (read(sd, msg, 1024) < 0) {
            perror("[client]Eroare la read() de la server.\n");
            return errno;
        }

        /* afisam mesajul primit */
        printf("[client]Mesajul primit este: %s\n", msg);
        if(strcmp(msg, "Conexiune incheiata") == 0)
            break;
        memset(mesaj,0,256);

        cin>>mesaj;

        /* trimiterea mesajului la server */
        if (write(sd, mesaj, 1024) <= 0) {
            perror("[client]Eroare la write() spre server.\n");
            return errno;
        }

    }
    /* inchidem conexiunea, am terminat */
    close (sd);
Run Code Online (Sandbox Code Playgroud)
  1. 服务器主代码和我发现问题的功能

#include "ConectareServer.h"
using namespace std;
/* portul folosit */
#define PORT 2825

/* codul de eroare returnat de anumite apeluri */
extern int errno;

typedef struct thData{
    int idThread; //id-ul thread-ului tinut in evidenta de acest program
    int cl; //descriptorul intors de accept
}thData;

static void *treat(void *); /* functia executata de fiecare thread ce realizeaza comunicarea cu clientii */
void raspunde(void *);

#pragma clang diagnostic push
#pragma ide diagnostic ignored "EndlessLoop"
int main ()
{
    struct sockaddr_in server;  // structura folosita de server
    struct sockaddr_in from;
    int nr;     //mesajul primit de trimis la client
    int sd;     //descriptorul de socket
    int pid;
    pthread_t th[100];    //Identificatorii thread-urilor care se vor crea
    int i=0;


    /* crearea unui socket */
    if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror ("[server]Eroare la socket().\n");
        return errno;
    }
    /* utilizarea optiunii SO_REUSEADDR */
    int on=1;
    setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));

    /* pregatirea structurilor de date */
    bzero (&server, sizeof (server));
    bzero (&from, sizeof (from));

    /* umplem structura folosita de server */
    /* stabilirea familiei de socket-uri */
    server.sin_family = AF_INET;
    /* acceptam orice adresa */
    server.sin_addr.s_addr = htonl (INADDR_ANY);
    /* utilizam un port utilizator */
    server.sin_port = htons (PORT);

    /* atasam socketul */
    if (bind (sd, (struct sockaddr *) &server, sizeof (struct sockaddr)) == -1)
    {
        perror ("[server]Eroare la bind().\n");
        return errno;
    }

    /* punem serverul sa asculte daca vin clienti sa se conecteze */
    if (listen (sd, 2) == -1)
    {
        perror ("[server]Eroare la listen().\n");
        return errno;
    }
    /* servim in mod concurent clientii...folosind thread-uri */
    while (1)
    {
        int client;
        thData * td; //parametru functia executata de thread
        int length = sizeof (from);

        printf ("[server]Asteptam la portul %d...\n",PORT);
        fflush (stdout);

        // client= malloc(sizeof(int));
        /* acceptam un client (stare blocanta pina la realizarea conexiunii) */
        if ( (client = accept (sd, (struct sockaddr *) &from, reinterpret_cast<socklen_t *>(&length))) < 0)
        {
            perror ("[server]Eroare la accept().\n");
            continue;
        }

        /* s-a realizat conexiunea, se astepta mesajul */

        // int idThread; //id-ul threadului
        // int cl; //descriptorul intors de accept

        td=(struct thData*)malloc(sizeof(struct thData));
        td->idThread=i++;
        td->cl=client;
        pthread_create(&th[i], NULL, &treat, td);

    }//while
};
#pragma clang diagnostic pop
static void *treat(void * arg)
{
    struct thData tdL;
    tdL= *((struct thData*)arg);
    printf ("[thread]- %d - Asteptam mesajul...\n", tdL.idThread);
    fflush (stdout);
    pthread_detach(pthread_self());
    raspunde((struct thData*)arg);
    /* am terminat cu acest client, inchidem conexiunea */
    printf ("[Server]Inchidem conexiunea cu threadul %d\n",tdL.idThread);
    close ((intptr_t)arg);
    return(NULL);

}


void raspunde(void *arg)
{
   
    struct thData tdL;
    tdL= *((struct thData*)arg);
    ConectareServer client;
    cout<<tdL.cl;
    client.Conectare(tdL.cl);

}
Run Code Online (Sandbox Code Playgroud)
void ConectareServer::Conectare(int arg) {
    fd=arg;
    cout<<fd;
    memset(mesaj_catre_client, 0, 1024);
    sprintf(mesaj_catre_client,
            "Conectare reusita, pentru a continua, trebuie sa te loghezi. Sintaxa comenzilor este urmatoarea: \n<login>, pentru a realiza logarea la server\n"
            "<quit>, pentru a inchide programul");
    write(arg, mesaj_catre_client, sizeof(mesaj_catre_client));
    VerifComanda(arg);
}
void ConectareServer::VerifComanda(int fd) {
    memset(mesaj_catre_client, 0, 1024);
    memset(mesaj_de_la_client, 0, 1024);
    read(fd, mesaj_de_la_client, sizeof(mesaj_de_la_client));
    printf("[server]Mesajul a fost receptionat...%s\n", mesaj_de_la_client);
    if(strcmp(mesaj_de_la_client, "login")==0){
        Logare client(fd);
        client.Login();
        cout<<"Intra in Logare";
    }
    if (strcmp(mesaj_de_la_client, "quit") == 0) {
        sprintf(mesaj_catre_client, "Conexiune incheiata");
        write(fd, mesaj_catre_client, sizeof(mesaj_catre_client));
    }
    if(strcmp(mesaj_de_la_client, "login") != 0 && strcmp(mesaj_de_la_client, "quit") != 0)
    {
        cout<<"Crash";
    }
}
Run Code Online (Sandbox Code Playgroud)

void Logare::Login() {
    memset(mesaj_catre_client, 0, 256);
    char user[10];
    char parola[8];
    sprintf(mesaj_catre_client,"Introduceti user-ul: ");
    write (fd, mesaj_catre_client, sizeof(mesaj_catre_client));
    read (fd, user, sizeof (user));

    printf ("[server]Mesajul a fost receptionat...%s\n", user);

    memset(mesaj_catre_client, 0, 256);
    sprintf(mesaj_catre_client,"Introduceti parola: ");
    write (fd, mesaj_catre_client, sizeof(mesaj_catre_client));

    read (fd, parola, sizeof (parola));
    printf ("[s]Mesajul a fost receptionat...%s\n", parola);

    if(Verificare_Logare(user,parola) == 1)
    {
        // Logare reusita
        Meniu client(fd, user);
        client.MeniuAplicatie();

    }
}
Run Code Online (Sandbox Code Playgroud)

我得到了登录和用户的正确读数,但是当涉及到 parola 的读取时,它在我可以在客户端窗口中输入输入之前只返回 0。

Sam*_*hik 6

显示的代码中有多个错误。read()返回 0 是观察到的问题中最少的。事实上,这可能只是所示代码中所有其他问题的最终结果,而不是真正的原因。第一个大问题多次出现,最好的例子是服务器代码:

write (fd, mesaj_catre_client, sizeof(mesaj_catre_client));
Run Code Online (Sandbox Code Playgroud)

此代码正在写入套接字。这完全忽略了来自write(). 写入套接字时,您绝对不能保证所有内容都将写入套接字。您想在sizeof(mesaj_catre_client)此处写入字节数。好吧,不幸的是,您不能保证所有这些字节都会被写入。write()可以只写第一个字节mesaj_catre_client并返回1。或者写这个结构的大约一半,并返回实际写入的字节数。您的代码必须检查它,但您的代码忽略了它,因此您并不真正知道此处写入了套接字的内容。

write() 必须检查每次调用的返回值,并相应地调整程序的逻辑。通常的解决方案是对write()剩余的字节重复, 这次。

从套接字读取时发生相同的逻辑错误:

if (read(sd, msg, 1024) < 0) {
Run Code Online (Sandbox Code Playgroud)

而且,再一次,无论如何,您绝对不能保证这将返回服务器发送的整个消息,但可能只是其中的前几个字节。即使服务器设法处理write()了整个消息,这里的第一次调用也可能只返回其中的一个字节,再次调用它将检索消息的其余部分(或其中的一些附加部分)。

反之亦然。如果服务器发送了多条消息,而它们最终都小于 1024 字节,你猜怎么着?你会read()一举获得它们。显示的代码根本没有准备好处理这个问题。

正确地从套接字读取和写入比起初看起来要困难得多。

所示代码的最后一组(潜在)问题:

td=(struct thData*)malloc(sizeof(struct thData));

// ...

static void *treat(void * arg)
{
    struct thData tdL;
    tdL= *((struct thData*)arg);
Run Code Online (Sandbox Code Playgroud)

显示的代码是 C++ 代码,它使用 C++ 类。现代 C++ 是类型安全的。没有现代 C++ 代码需要使用mallocfree,或转换为/从void *. 这仅在此处是必需的,因为您使用的是 C API。这应该替换为 type-safe std::thread,它应该不是必需的mallocnew任何东西。

但是所显示代码的根本问题是它没有准备好并且不能正确处理套接字的读/写语义,这对于在每个read()write()调用上读取和写入的内容几乎没有保证。您不能做出任何类型的假设,您必须仔细实现从套接字写入和读取单个字节的正确逻辑,并将它们组装到读取和写入的逻辑结构中。既read()不会也write()不会为您这样做,有必要自己实现所有逻辑。