2016-08-14 15 views
5

Próbuję wykonać próbkę serwera w języku C++ przy użyciu <sys/socket.h> i konstruktora GUI Qt Creator, ale dwa dziwne zachowania występują w warstwie gniazda programu. Po pierwsze, ja uruchomić serwer, ale przy pierwszej próbie robię aby połączyć go za pomocą telnet jest imediately zamknięteGniazdo C++ zamykające pierwszą próbę połączenia

Trying 127.0.0.1 ... Connected
do 127.0.0.1.
Znak ucieczki to "^]".
Połączenie zamknięte przez hosta zagranicznego.

Kiedy próbuję połączyć się po raz drugi, działa i terminal czeka na moje wejście. Drugą rzeczą jest, gdy zamykam połączenie. Gdybym ponownie uruchomić zaraz po, w ciągu kilku minut, program zatrzymuje się na bind wyjściu i powrocie:

błąd na wiązanie: Address already in use

Więc przypuszczam, może połączenie odbywa się po tym, jak go złamam, używając funkcji onCortarConexao() lub po prostu zatrzymania debuggera. W każdym razie, czego mi brakuje?

Mój kod:

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

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

Na MainWindow.cpp: Klasa

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

Gniazdo: realizacja

#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 

Gniazdo:

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); 

} 

Kompletny kod jest pod adresem: https://github.com/FabioNevesRezende/BasicCppServer

Edit 1:

Więc this lista pakietów komunikacji między telnet i mojej aplikacji Qt Server można graphicaly widać w WireShark (plik .pcapng). Zawiera 11 klatek. Sześć pierwszych pochodzi z pierwszego telnetu, gdy jest on natychmiast zamykany. Jak się wydaje na klatkach 4 i 5, gdzie aplikacja wysyła [FIN, ACK], a serwer odpowiada na nią, zamykając połączenie. Ramki 7, 8, 9 są drugą próbą połączenia, a ramki 10 i 11 są wysyłane do serwera przez serwer abc. Jak na ekranie wydruku:

sequence of commands

Problem polega na tym, że nie wiem, dlaczego aplikacja wysyła ten FIN i gdzie w kodzie jest.

+0

Podany przez Ciebie link do projektu github jest uszkodzony. Bez tego nie mogę stwierdzić, czy to, co masz na myśli przez drugą próbę, to po prostu próbować ponownie po stronie klienta lub zrestartować serwer. Czy mógłbyś wyjaśnić? – lasaro

+0

Przepraszam, jest ponownie włączony. – Fabiotk

Odpowiedz

4

Zastosowanie SO_REUSEADDR z setsockopt:

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

Pozwala to port do ponownego wykorzystania przez innych gniazd i dostaje wokół kwestii patrzysz address already in use.

+0

Dzięki, rozwiązał drugi problem. – Fabiotk

+1

Możesz chcieć wyjaśnić, dlaczego to działa, aby było to dobre Q + A dla przyszłych odwiedzających. –

3

Oto pierwszy poważny problem znalazłem:

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

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

Nie ma obsługa dla read powrocie do zera. Specyfikator formatujest przeznaczony tylko dla łańcuchów. Nie można go używać do arbitralnych, niezaznaczonych danych.

3

W systemach operacyjnych UNIX, wywołania systemowe mogą czasami zawieść z errorno ustawionym na EINTR. Oznacza to, że sygnał został dostarczony do procesu, który przerwał wywołanie systemowe i powinieneś spróbować go ponownie. Wypróbuj coś takiego:

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

To samo powinno być zrobione w przypadku wezwania do napisania.

5

Poprzednie odpowiedzi wskazują, że należy postępować poprawnie z odczytem i jak korzystać z SO_RESUEADDR. Chcę wskazać, dlaczego program może otrzymać tylko jedną wiadomość. ponieważ twoja funkcja run nie ma pętli, więc może być wykonana tylko raz, a następnie wątek wychodzi. po zaakceptowaniu żądania klienta, połączenie zostaje ustanowione, następnie należy wprowadzić pętlę, aby odczekać dane wejściowe klienta, i zapisać je po powrocie funkcji odczytu.

+0

dobry punkt. Całkowicie tego nie zauważyłem. –

Powiązane problemy