简体   繁体   中英

c++ socket closing the first connection attempt

I'm trying to make a server sample in c++ using <sys/socket.h> and Qt Creator gui builder but two weird behaviors are going on at socket layer of the program. First of, I run the server but at the first attempt I make to connect to it using telnet is imediately closed

Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Connection closed by foreign host.

When I attempt connection for the second time, it works and the terminal waits for my input. The second thing is when I close the connection. If I rerun right after, in a matter of minutes, the program halts on bind exiting and returning:

ERROR on binding: Address already in use

So I suppose maybe a connection is being held after I break it using function call onCortarConexao() or just stopping the debugger. Anyway, what am I missing?

My code:

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

On MainWindow.cpp:

void MainWindow::on_pushButton_clicked()
{
    socket1 = new MSocket();
    socket1->start();
}
void MainWindow::on_pushButton_3_clicked()
{
    socket1->onCortarConexao();
}

Socket class:

#ifndef MSOCKET_H
#define MSOCKET_H
#include <QString>
#include <QObject>
#include <QThread>
#include <QList>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#define SERVER_BUFFER 4096
#define PORTRUN 15000


class MSocket : public QThread
{
public:
    MSocket();
    void error(char *msg);
    void onCortarConexao();

private:
    int sockfd;
    int newsockfd;
    int portno;
    int clilen;
    int n;
    char buffer[SERVER_BUFFER];
    struct sockaddr_in serv_addr, cli_addr;
    u_short port;
    void run();
};

#endif // MSOCKET_H

Socket implementation:

void MSocket::run()
{
sockfd = socket(AF_INET, SOCK_STREAM, 0);

if (sockfd < 0){
    error("ERROR opening socket");
    exit(-1);
}
bzero((char *) &serv_addr, sizeof(serv_addr));

portno = PORTRUN;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portno);
serv_addr.sin_addr.s_addr = INADDR_ANY;

fprintf(stdout,"Iniciando servidor..");
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0){

    error("ERROR on binding");
    exit(-1);
}
listen(sockfd,5);

newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, (socklen_t*) sizeof(cli_addr));
if (newsockfd < 0){
    error("ERROR on accept");
    exit(-1);
}

bzero(buffer,SERVER_BUFFER);
n = read(newsockfd,buffer,SERVER_BUFFER-1);
if (n < 0)
     error("ERROR reading from socket");

printf("Here is the message: %s",buffer);

n = write(newsockfd,"I got your message",18);
if (n < 0)
     error("ERROR writing to socket");




}

void MSocket::onCortarConexao(){
    printf("Encerrando socket");
    close(newsockfd);
    close(sockfd);

}

The complete code is at: https://github.com/FabioNevesRezende/BasicCppServer

Edit 1:

So, this is the list of packets of the communication between telnet and my Qt Server application it can be graphicaly seen in WireShark (.pcapng file). It contains 11 frames. The 6 first ones are from the first telnet, when it is imediately closed. As it seems on frame 4 and 5 where the application sends [FIN, ACK] and the server responds to it by closing the connection. The frames 7,8,9 are the second attempt to connect and frames 10 and 11 is when I send the abc to the server. As in the print screen:

命令序列

The problem is I don't know why the application is sending this FIN and where in the code it is.

Use SO_REUSEADDR with setsockopt :

optval = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);

This allows the port to be reused by other sockets and gets around the address already in use issue you're facing.

The previous answers have indicate that how to handle the read correctly and how to use SO_RESUEADDR. I want to point out why your program can just receive one message. because your run function doesn't have a loop, so it can be only performed once, then the thread exits. after accept a client's request, then the connection is establish, then you should enter a loop to wait the client's input, and write them back once the read function returns.

Here's the first serious problem I found:

n = read(newsockfd,buffer,SERVER_BUFFER-1);
if (n < 0)
     error("ERROR reading from socket");

printf("Here is the message: %s",buffer);

There's no handling for read returning zero. The %s format specifier is only for strings. You can't use it for arbitrary, unchecked data.

On unix operating systems, system calls can sometimes fail with an errorno set to EINTR. This means that a signal was delivered to the process which interrupted the system call and you should retry it. Try something like this:

while  ((n = read(newsockfd,buffer,SERVER_BUFFER-1) < 0) {
 if (errno != EINTR)
 break;
 }
if (n < 0)
     error("ERROR reading from socket");

The same should be done for the call to write.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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