2013-12-03 113 views
0

我正在一個大學項目中,我必須將樹莓派連接到Android智能手機來控制2個電機。 我們是套接字編程的新手,所以我們從一個我們在wikibook上找到的例子開始,並試圖根據我們的需要進行修改。我們現在面臨的問題是,服務器和客戶端之間的連接非常隨意且不穩定,有時會連接,並且在短暫的斷開連接之後不會再次連接。 (對我來說),奇怪的是,那之後我們編輯負責連接部分上面的代碼:套接字任意連接 - 或沒有

/* bind serv information to mysocket */ 
bind(mysocket, (struct sockaddr *)&serv, sizeof(struct sockaddr)); 

/* start listening, allowing a queue of up to 2 pending connection */ 
listen(mysocket, 2); 
consocket = accept(mysocket, (struct sockaddr *)&dest, &socksize); 

就像一個printf插入,接下來我們推出PROGRAMM時間,寄託都沒有工作,有時是兩個或三次,然後它就停止連接。

我搜遍了谷歌所有類似的問題,但我還沒有找到相應的,所以我現在直接轉向你。

這是我們的服務器上的樹莓派,這也成爲網絡熱點運行的代碼:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <arpa/inet.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <sys/socket.h> 
#include <bcm2835.h> 

#define PORTNUM 5298 
#define MAXRCVLEN 1000 

#define PIN9 RPI_GPIO_P1_21 
#define PIN10 RPI_GPIO_P1_19 
#define PIN11 RPI_GPIO_P1_23 
#define PIN22 RPI_GPIO_P1_15 


int setpins(); 
int forward(); 
int backward(); 

int main(int argc, char *argv[]) 
{ 
char msg[] = "Connected!\n"; 
char testchar[] = "stillthere?"; 
char quitstring[] = "quit"; 
char *recbuf; 

int qflag = 0; 
int lflag = 0; 
int mysocket, consocket, len;  /* socket used to listen for incoming connections */ 

struct sockaddr_in dest;  /* socket info about the machine connecting to us */ 
struct sockaddr_in serv;  /* socket info about our server */ 

socklen_t socksize = sizeof(struct sockaddr_in); 


memset(&serv, 0, sizeof(serv));   /* zero the struct before filling the fields */ 
serv.sin_family = AF_INET;    /* set the type of connection to TCP/IP */ 
serv.sin_addr.s_addr = htonl(INADDR_ANY); /* set our address to any interface */ 
serv.sin_port = htons(PORTNUM);   /* set the server port number */ 

mysocket = socket(AF_INET, SOCK_STREAM, 0); 

/* bind serv information to mysocket */ 
bind(mysocket, (struct sockaddr *)&serv, sizeof(struct sockaddr)); 
/* start listening, allowing a queue of up to 2 pending connection */ 
listen(mysocket, 2); 
consocket = accept(mysocket, (struct sockaddr *)&dest, &socksize); 


if (!bcm2835_init()) return 1; 

setpins(); 
while(consocket) 
{ 
    printf("Incoming connection from %s - sending welcome\n", inet_ntoa(dest.sin_addr)); 
    send(consocket, msg, strlen(msg), 0); 

    while (!qflag && !lflag) { 

     // Do something when connection is lost: SO_KEEPALIVE? 
    // if (!send(consocket,testchar, strlen(testchar), 0)) lflag = 1; 
      recbuf = malloc (MAXRCVLEN+1); 
      len = recv(consocket, recbuf, MAXRCVLEN, 0); 
      recbuf[len] = '\0'; 

    if (len > 0) printf("Client sent %s (%d bytes). \n", recbuf, len); 
      if (recbuf[0] == 'v') forward(); // this function lets our car drive forward 
      if (recbuf[0] == 'r') backward();// this one backwards ;) 
    // Leave this loop if the client sends you the quitstring 
      if (!strcmp (recbuf, quitstring))  qflag = 1; 

    free(recbuf); 
    } 

if (qflag) break; 
    listen(mysocket, 1); 
    consocket = accept(mysocket, (struct sockaddr *)&dest, &socksize); 
} 

close(consocket); 
close(mysocket); 
printf("sockets closed\n"); 
return EXIT_SUCCESS; 
} 

一個在那裏線

// if (!send(consocket,testchar, strlen(testchar), 0)) lflag = 1; 

是我們的理念,以測試羯羊連接仍然存在,這是可行的嗎?

這是客戶端代碼,那不是在Java中,但還沒有在C:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <arpa/inet.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <sys/socket.h> 

#define MAXRCVLEN 500 
#define PORTNUM 5298 

int main(int argc, char *argv[]) 
{ 
    char buffer[MAXRCVLEN + 1]; /* +1 so we can add null terminator */ 
    int len, mysocket; 
    struct sockaddr_in dest; 

    mysocket = socket(AF_INET, SOCK_STREAM, 0); 

    memset(&dest, 0, sizeof(dest));    /* zero the struct */ 
    dest.sin_family = AF_INET; 
    dest.sin_addr.s_addr = inet_addr("192.168.42.1"); /* set destination IP number */ 
    dest.sin_port = htons(PORTNUM);    /* set destination port number */ 

    do { 
    connect(mysocket, (struct sockaddr *)&dest, sizeof(struct sockaddr)); 
    len = recv(mysocket, buffer, MAXRCVLEN, 0); 
    }while(len < 0); 
    /* We have to null terminate the received data ourselves */ 
    buffer[len] = '\0'; 

    // Received 
    printf("Received %s (%d bytes).\n", buffer, len); 

    // send: 
    char msg[] = " "; 
    do{ 
    scanf("%s",msg);  
    printf("Sending Msg to %s \n", inet_ntoa(dest.sin_addr)); 
    send(mysocket, msg, strlen(msg),0); 
    }while (strcmp(msg,"quit")); 


    close(mysocket); 
    return EXIT_SUCCESS; 
} 

任何想法,我們做錯了什麼?

在此先感謝!

+1

總是檢查所有相關係統調用的返回代碼。 – alk

+0

仔細閱讀recv()/ send()的手冊頁,並瞭解到至少對於套接字來說,這兩個函數不一定會收到/發送儘可能多的字節,而只是很少。因此,圍繞此類呼叫進行計數,直到收到/發送所有數據都是一個好主意,而不是說必不可少。 – alk

+0

此外,'read()'返回'0'的情況應該被分開處理,因爲它指示另一端'close()'連接。 – alk

回答

0

除非你真正,真正要了解什麼是低層次的Berkeley套接字操作,我建議你看一下libevent或類似的圖書館。

你的主循環的結構有點不尋常。您一次只能處理一個連接,並且不能很好地處理您在服務之前連接時發生的任何連接嘗試。

bind(mysocket, (struct sockaddr *)&serv, sizeof(struct sockaddr)); 

綁定可能失敗,例如,如果另一個進程最近打開了套接字並且操作系統還沒有完成清理端口的使用。你可以改變這種行爲,但你還是應該檢查,從die.net's bind manpage

Return Value 
    On success, zero is returned. On error, -1 is returned, and errno is set appropriately. 

所以

if(bind(mysocket, (struct sockaddr *)&serv, sizeof(struct sockaddr))) { 
    perror("bind failed"); 
    exit(1); 
} 

聽()只需要調用一次,而且還需要檢查

if(listen(mysocket, 2)) { 
    perror("listen failed"); 
    exit(1); 
} 

之後,如果您滿意地做單服務方式,那麼您可以執行以下操作:

012然後

readLoop會是這樣的:

int readLoop(int socket, char* buffer, size_t bufSize) { 
    int len = recv(socket, buffer, bufSize); 
    if (len > 0) 
     return processBuffer(socket, buffer, len); 
    if (len < 0 && (errno == EINTR || errno == EAGAIN)) 
     return 0; // do-over 
    return -1; 
} 

確保processBuffer也返回0或相應-1。

正如我上面提到的那樣,這種方法仍然存在問題,但這不是我的意圖,在這裏教你一切你需要知道關於套接字的一切:)如果你想進一步開發你的套接字知識,你的下一個停止應該學習有關非阻塞套接字的selectpoll,以便您可以託管多個套接字並在它們變爲活動狀態時爲它們提供服務。

+0

哇,非常感謝你的詳細答案!當我們有時間分配時,我們會在幾個小時內嘗試。我猜它有時連接的原因,有時並沒有,因爲操作系統沒有真正關閉套接字,我們也沒有檢查錯誤。 – Sami

+0

好的,除了processBuffer方法之外,我們還提供了大部分您所建議的更改,因爲我們並沒有真正瞭解它應該做什麼。我的意思是,recv已經將所有接收到的字節寫入緩衝區,爲什麼不直接返回len? processBuffer中應該發生什麼,需要套接字嗎? – Sami

+0

它可能想'發送()'一個響應。 processBuffer將用於檢查已收到的數據並選擇反應。 – kfsone

0

通常,您應該使用tcpdump/wireshark來查看您的Rpi和strace看到的數據包,以查看您的程序的功能。我對連接有時不起作用的第一次猜測是數據包丟失。通過使用有線LAN(以太網),您可以排除這種可能性。

但是您使用的示例服務器代碼是一個相當糟糕的例子。即使您一次只想接受一個客戶端連接,您的服務器也不應使用阻止等待任何遠程消息。您應該閱讀有關使用非阻塞I/O,selectpoll,並查看使用這些示例的示例。另外,請閱讀SO_REUSEADDR,你可能也需要在你的服務器上安裝一個。

+0

我肯定會閱讀關於選擇和輪詢,那些甚至沒有出現在我讀的教程中... – Sami

0

這行代碼

char msg[] = " "; 
do{ 
    scanf("%s",msg); 

如果在掃描的字節數越大則1個字符,如msg提供恰好兩個字節(從其中一個始終用作0終止子)就會失敗。餵食更多會寫出msg的界限,這樣做會引發未定義的行爲。

爲了解決這個問題提供至少一個最低的255個字符這麼:

char msg[256] = ""; 
do{ 
    scanf("%255s",msg); 
+0

是的,我們並沒有真正考慮這個,因爲它是我們使用的參考代碼的剩餘部分,修改後,我不認爲這會在最後得到它:) 謝謝:) – Sami