How to exchange information in full-duplex mode?

Asked

Viewed 145 times

7

Note the two codes below:

Client. c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define SRV_ADDR "127.0.0.1"
#define SRV_PORT "9009"

int make_socket(void){

    int ecode, sockfd;

    struct addrinfo *results=NULL, hints;

    memset(&hints, 0, sizeof(struct addrinfo));

    hints.ai_family=AF_INET;
    hints.ai_socktype=SOCK_STREAM;
    hints.ai_protocol=IPPROTO_TCP;

    if((ecode=getaddrinfo(SRV_ADDR, SRV_PORT, &hints, &results))!=0){

        sockfd=-1;

    }else{

        struct addrinfo *it=NULL;

        for(it=results; it!=NULL; it=it->ai_next){

            if((sockfd=socket(it->ai_family, it->ai_socktype, it->ai_protocol))==-1){

                continue;
            }

            if(connect(sockfd, it->ai_addr, it->ai_addrlen)==0){

                break;
            }

            close(sockfd);
        }

        freeaddrinfo(results);

        if(it==NULL){

            sockfd=-1;
        }
    }

    return sockfd;
}

short get_msg(char *msg, size_t n){

    int rv;

    if(fgets(msg, n, stdin)!=NULL){

        size_t len=strlen(msg);

        msg[len-1]='\0'; //elimina o \n

        rv=0;

    }else{

        rv=-1;
    }

    return rv;
}

int main(void){

    int sockfd=make_socket();

    if(sockfd!=-1){

        short rv;
        char msg[101];

        printf("\n");

        do{

            do{

                printf("Você >");

                if((rv=get_msg(msg, 101))!=0){

                    printf("\n* Falha na leitura!");
                    printf("\n  |.__Escreva a mensagem novamente.\n\n");
                }

                send(sockfd, msg, 101, 0);

            }while(rv!=0);

            if(strcmp(msg, "!exit")!=0){

                recv(sockfd, msg, 101, 0);

                printf("Estranho: %s\n", msg);
            }

        }while(strcmp(msg, "!exit")!=0);

        close(sockfd);
    }

    return 0;
}

Server. c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int make_socket(void){

    int sockfd;
    struct addrinfo *results=NULL, hints;

    memset(&hints, 0, sizeof(struct addrinfo));

    hints.ai_flags=AI_PASSIVE;
    hints.ai_family=AF_INET;
    hints.ai_socktype=SOCK_STREAM;
    hints.ai_protocol=IPPROTO_TCP;

    if(getaddrinfo(NULL, "9009", &hints, &results)!=0){

         sockfd=-1;

    }else{

          struct addrinfo *it=NULL;

          for(it=results; it!=NULL; it=it->ai_next){

              if((sockfd=socket(it->ai_family, it->ai_socktype, it->ai_protocol))==-1){

                  continue;
              }

              if(bind(sockfd, it->ai_addr, it->ai_addrlen)==0){

                  break;
              }

              close(sockfd);
          }

          freeaddrinfo(results);

          if(it==NULL){

              sockfd=-1;

          }else{

              if(listen(sockfd, 1)==-1){

                  sockfd=-1;
              }
          }
    }

    return sockfd;
}

short get_msg(char *msg, size_t n){

    int rv;

    if(fgets(msg, n, stdin)!=NULL){

        size_t len=strlen(msg);

        msg[len-1]='\0'; //elimina o \n

        rv=0;

    }else{

        rv=-1;
    }

    return rv;
}

int main(void){

    int sockfd, csockfd;

    if((sockfd=make_socket())!=-1){

        if((csockfd=accept(sockfd, NULL, NULL))!=-1){

            short rv;
            char msg[101];

            printf("\n");

            do{

                recv(csockfd, msg, 101, 0);

                printf("Estranho: %s\n", msg);

                if(strcmp(msg, "!exit")!=0){

                    do{

                        printf("Você >");

                        if((rv=get_msg(msg, 101))!=0){

                            printf("\n* Falha na leitura!");
                            printf("\n  |.__Escreva a mensagem novamente.\n\n");
                        }

                        send(csockfd, msg, 101, 0);

                    }while(rv!=0);
                }

            }while(strcmp(msg, "!exit")!=0);

            close(csockfd);
        }

        close(sockfd);
    }

    return 0;
}

Now observe the execution of both:

Client:

t-002@localhost:~/Documents/C$ gcc -Wall client.c -o client
t-002@localhost:~/Documents/C$ ./client

Você >Olá 
Estranho: Não posso conversar agora :(
Você >Okay amigo :(
Estranho: !exit

Server:

t-002@localhost:~/Documents/C$ gcc -Wall server.c -o server
t-002@localhost:~/Documents/C$ ./server

Estranho: Olá
Você >Não posso conversar agora :(
Estranho: Okay amigo :(
Você >!exit

Did you notice that the communication between the server and the client was done in a double sense, but not in a simultaneous way? Well, this form of data exchange is called half-duplex. A more practical example of this are the walkie-talkies:

walkie-talkies

Here is observed the following: While one of the walkie-talkies speaks the other will only be able to hear, ie, the data transmission occurs as a function of time. It is almost the same thing as my server and client up there, but there it was pre-established who spoke first and on top of that it was necessary to wait for the other to reply to be able to speak back.

Therefore, I have the following question: It is possible to establish a connection between a client and a server written/programmed in C where the data is exchanged in a double or two-way simutaneous, ie in mode full-duplex?

1 answer

0


One way to do this is to create two threads (or two child processes on Unix/Linux), one thread receiving and another sending.

1 server pseudo-code that handles only 1 connection and then terminates, but sends and receives "concurrent":

1. cria socket  
2. faz bind
3. faz listen
4. faz accept
5. cria thread de recepção
6. cria thread de envio
7. fica parado na thread principal (por exemplo, faz um getchar)

Both the receiving thread and the sending thread use the same socket returned by the Accept function.

Receiving thread:

1. faz receive
2. se retornou erro ou fim de conexão, finaliza programa (chama exit)
3. não foi erro nem fim da conexão, então processa dados recebidos
4. volta para 1

Sending thread:

1. gera dados a serem enviados
2. faz send
3. se retornou erro, então finaliza programa (chama exit)
4. volta para 1

Another, and probably the most common, way to do this is by using event programming.

In this case, you use a function of the TCP stack called select (which in practice is now obsolete), or a function of s.o. (epoll on Linux, or Getqueuedcompletionstatus on Windows) to check if there is an event pending in the connection socket.

This "event" can be related to receiving or sending.

In Linux a receiving event means that a reception can be made in the socket in question, which can (but is not guaranteed) have data ready to be received. And a shipping event means that it can (but is also not guaranteed) be sent to the socket in question. As there is no guarantee that a sending or receiving operation can be attempted, the socket is used in "non-blocking" mode, so in receiving or sending the program is not blocked to wait for the operation to be performed.

On Linux the operation is "reactive". That is, the program asks for s.o. (through select or epoll) whether it can perform a receiving or sending operation, and when it receives an event from s.o. then the program attempts to perform the operation.

On Windows, on the other hand, the operation is "proactive". The program creates the connection ready to work with IOCP, and then performs the operations. The program doesn’t stand still until the operation is over. The program knows that the operation ended when it receives an event through the Getqueuedcompletionstatus function for the operation that was started previously.

In practice, both on Linux and Windows event programming is done using threads, having a thread or more reserved for handling the events received through epoll or Getqueuedcompletionstatus.

In this link (found in Google) is a simple example of using epoll without additional threads in Linux, and in this other link a recipe and a step-by-step example of a server (also not using additional threads) with IOCP on Windows.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.