2015-01-14 113 views
0

我試圖從頭開始重新創建一個GUI工作,它將成爲我的第一個網絡應用程序。爲此,我一直在閱讀所有可以找到的C/C++套接字教程信息,但我已經用自己的信息將自己編碼爲無用的幾百行代碼。基本的C++套接字客戶端

有兩個問題(據我所知),用下面的代碼:

1)當它連接到我應該被偵聽的端口,就應該立即收到字符串99999999%connect。此時,我應該能夠發送0並開始接收當天數據的開始。但是,我沒有收到第一條消息。

2)我不知道如何在這樣一種方式,它不會導致GTK UI鎖住寫這個。我知道,這是一些基本的東西,但是這整個事情只是在套接字上進行的第一次嘗試。

總之,這裏的代碼:

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

#include <sys/types.h> 
#include <sys/time.h> 

#include <sys/socket.h> 
#include <netdb.h> 
#include <arpa/inet.h> 

#include <gtk/gtk.h> 

#define UNUSED(x) (void) x 

typedef struct 
{ 
    GtkWidget *window, *grid, 
      *labelHost, *labelPort, *textFieldHost, *textFieldPort, 
      *buttonConnect; 
}uiWidgets; 

int sockfd = 0; 

gboolean on_close_cleanup(); 
void buttonConnect_on_clicked(GtkButton *button, uiWidgets* widgets); 
void connect_to_logview(struct addrinfo *hostname_result); 
void listen_for_logview(); 

int main() 
{ 
    uiWidgets widgets; 

    gtk_init(NULL, NULL); 

    widgets.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 
    gtk_window_set_title(GTK_WINDOW(widgets.window), "SG GUI V3.00"); 
    g_signal_connect(widgets.window, "destroy", gtk_main_quit, nullptr); 
    g_signal_connect(widgets.window, "delete-event", G_CALLBACK(on_close_cleanup), 
        nullptr); 

    widgets.labelHost = gtk_label_new("Host"); 
    widgets.labelPort = gtk_label_new("Port"); 

    widgets.textFieldHost = gtk_entry_new(); 
    widgets.textFieldPort = gtk_entry_new(); 

    widgets.buttonConnect = gtk_button_new_with_label("Connect"); 
    g_signal_connect(widgets.buttonConnect, "clicked", 
        G_CALLBACK(buttonConnect_on_clicked), 
        &widgets); 

    widgets.grid = gtk_grid_new(); 
    gtk_grid_set_row_spacing(GTK_GRID(widgets.grid), 5); 
    gtk_grid_set_column_homogeneous(GTK_GRID(widgets.grid), true); 
    gtk_grid_set_row_homogeneous(GTK_GRID(widgets.grid), true); 

    gtk_grid_attach(GTK_GRID(widgets.grid), widgets.labelHost, 0, 0, 1, 1); 
    gtk_grid_attach(GTK_GRID(widgets.grid), widgets.labelPort, 2, 0, 1, 1); 
    gtk_grid_attach(GTK_GRID(widgets.grid), widgets.textFieldHost, 0, 1, 2, 1); 
    gtk_grid_attach(GTK_GRID(widgets.grid), widgets.textFieldPort, 2, 1, 1, 1); 
    gtk_grid_attach(GTK_GRID(widgets.grid), widgets.buttonConnect, 1, 2, 1, 1); 

    gtk_container_add(GTK_CONTAINER(widgets.window), widgets.grid); 
    gtk_widget_show_all(widgets.window); 

    gtk_main(); 

    return 0; 
} 

gboolean on_close_cleanup() 
{ 
    close(sockfd); 
    return false; 
} 

void buttonConnect_on_clicked(GtkButton *button, uiWidgets* widgets) 
{ 
    UNUSED(button); 
    struct addrinfo hints, *hostname_result; 
    struct sockaddr_in *hostname_address; 
    const gchar *hostname = gtk_entry_get_text(GTK_ENTRY(widgets->textFieldHost)); 
    int return_value; 
    gchar addr[INET_ADDRSTRLEN]; 

    memset(&hints, 0, sizeof(hints)); 
    hints.ai_family = AF_INET; 
    hints.ai_socktype = SOCK_STREAM; 

    if((return_value = getaddrinfo(hostname, 
          gtk_entry_get_text(GTK_ENTRY(widgets->textFieldPort)), 
          &hints, &hostname_result)) != 0) 
    { 
    g_print("getaddrinfo: %s\n", gai_strerror(return_value)); 
    return; 
    } 

    for(struct addrinfo *info_itr = hostname_result; info_itr != nullptr; 
     info_itr = info_itr->ai_next) 
    { 
    hostname_address = (struct sockaddr_in *)info_itr->ai_addr; 
    inet_ntop(AF_INET, &(hostname_address->sin_addr), addr, INET_ADDRSTRLEN); 

    g_print("%s:%s\n", addr, gtk_entry_get_text(GTK_ENTRY(widgets->textFieldPort))); 
    } 

    connect_to_logview(hostname_result); 

    freeaddrinfo(hostname_result); 
} 

void connect_to_logview(struct addrinfo *hostname_result) 
{ 
    int rv = 0; 
    sockfd = socket(hostname_result->ai_family, hostname_result->ai_socktype, 
         hostname_result->ai_protocol); 

    if(sockfd == -1) 
    { 
    perror("socket"); 
    } 
    else 
    { 
    rv = connect(sockfd, hostname_result->ai_addr, hostname_result->ai_addrlen); 

    if(rv == -1) 
    { 
     perror("connect"); 
    } 
    else 
    { 
     g_print("%s: Looks like we got something!\n", __FUNCTION__); 
     listen_for_logview(); 
    } 
    } 
} 

void listen_for_logview() 
{ 
    fd_set fdset; 
    gchar buf[256]; 
    int nbytes = 0; 
    bool exit = false; 
    struct timeval tv; 

    tv.tv_sec = 2; 
    tv.tv_usec = 0; 

    FD_ZERO(&fdset); 

    while(!exit) 
    { 
    if(select(sockfd+1, &fdset, NULL, NULL, &tv) == -1) 
    { 
     perror("select"); 
     exit = true; 
     break; 
    } 

    if(FD_ISSET(sockfd, &fdset)) 
    { 
     if((nbytes = recv(sockfd, buf, sizeof buf, 0)) <= 0) 
     { 
     perror("recv"); 
     close(sockfd); 
     FD_CLR(sockfd, &fdset); 
     exit = true; 
     break; 
     } 
     else 
     { 
     g_print("!!!\n%s\n\n", buf); 
     close(sockfd); 
     FD_CLR(sockfd, &fdset); 
     exit = true; 
     break; 
     } 
    } 
    else 
    { 
     g_print("Timed out!\n"); 
     tv.tv_sec = 2; 
    } 
    } 
} 

所以,這裏有什麼錯?我猜測答案是我只是濫用我所擁有的工具。除了上述兩種不當行爲之外,我還要感謝您在這裏看到的任何其他結構/技術問題。

在此先感謝您的幫助。

+1

我把你的插座碼出你的UI代碼和一個整潔的小它自己的階級包起來,你可以分別測試。 – Galik

+0

一旦我開始正確聽音,那肯定是我的方向。目前,只是爲了簡單起見,它們都在一個文件中。一旦我知道我必須做什麼來傾聽,我會從實際的對象/等開始。 – musasabi

+0

GTK是一個古老的C庫,而不是C++。 Qt是一個C++版本。 –

回答

0

你歸零FD集,從來沒有把任何東西進去,所以你在一個空的FD集中選擇,所以從來都沒有被選定爲可讀,可寫什麼,所以你從來沒有真正閱讀。您需要將sockfd添加到FD集合,並使用相應的宏FD_*

您還需要在每次循環時重置超時結構。

但是當你使用的是阻塞模式,有沒有必要使用select()可言。只要做recv()。它會阻止。

NB如果recv()返回零它是流的末尾:對等體已斷開。這不是一個錯誤,在這種情況下調用perror()將只打印一個隨機錯誤消息。不要這樣做。

+0

謝謝。這很明顯,顯然有些失蹤。我會盡快嘗試一下。但是,一個問題是:使用非阻塞套接字是否會解決GUI鎖定問題? – musasabi

+0

我剛測試過它,它確實有效。儘管如此,我仍然想回聽阻塞/非阻塞查詢。 =) – musasabi

+0

@musasabi GUI鎖定是通過在GUI線程中執行阻塞操作引起的。 'select()'是一個阻塞操作。解決方案是在單獨的線程中執行網絡操作。 – EJP

-1

你似乎使用阻塞套接字。 connect會阻止線程直到連接或失敗。

您需要使用,這樣的連接建立異步非阻塞套接字。

+0

會通過fcntl(),F_SETFL和O_NONBLOCK嗎? – musasabi

+0

@musasabi http://www.kegel.com/dkftpbench/nonblocking.html –

+0

@musasabi如果你想連接異步建立,你需要使用非阻塞套接字*。*在你的問題中沒有關於這一點的信息,所以沒有必要遵循這個建議。事實上,「異步」是在這裏使用的錯誤術語。所有非阻塞模式連接都可以讓您縮短超時時間。 – EJP