0

私は 2 つのモーターを制御するためにラズベリー pi を Android スマートフォンに接続する必要がある大学のプロジェクトに取り組んでいます。ソケットプログラミングは初めてなので、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 に挿入するのと同じように、次にプログラムを起動すると、すべてが機能し、時には 2 ~ 3 回機能し、その後接続が停止します。

私はグーグル全体を検索したので、同様の問題を探しましたが、同等の問題が見つからなかったので、直接あなたに連絡します。

これは、ネットワーク ホットスポットとしても機能するラズベリー パイで実行されているサーバーのコードです。

#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;
}

私たちが間違っていたことについて何か考えはありますか?

前もって感謝します!

4

3 に答える 3

0

あなたが実際に学びたいのが低レベルのバークレー ソケット操作でない限り、libeventまたは同様のライブラリを参照することをお勧めします。

メイン ループの構造は少し変わっています。一度に 1 つの接続しか処理できないことは明らかであり、以前の接続にサービスを提供している間に発生した接続試行にはうまく対処できません。

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

たとえば、別のプロセスがソケットを最近開いていて、OS がポートの使用のクリーンアップを完了していない場合などです。この動作は変更できますが、die.net の bind マンページから確認する必要があります。

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

listen() は一度だけ呼び出す必要がありますが、チェックする必要もあります

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

この後、単一サービスのアプローチを行うことに満足している場合は、次のことができます。

mysocket = socket(AF_INET, SOCK_STREAM, 0);
if(mysocket < 0) {
    perror("socket failed");
    exit(1);
}

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

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

for (;;) {
    consocket = accept(mysocket, (struct sockaddr *)&dest, &socksize);
    if(consocket < 0) // might return if the connection has already gone away.
        continue;
    if (!sendGreeting(consocket)) {
        // sendGreeting should return -1 if it was unable to send, 0 if successful
        while (!readLoop(consocket, recvBuf, MAXRCVLEN))
            ;
    }
    close(consocket);
}

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 を返すようにしてください。

上で述べたように、このアプローチにはまだ問題がありますが、ここでソケットについて知っておくべきことすべてを一度に教えることは私の意図ではありません :) ソケットの知識をさらに深めたい場合は、次の目的地は学習する必要があります。複数のソケットをホストし、それらがアクティブになったときにサービスを提供できるように、ノンブロッキング ソケットを使用した選択またはポーリングについて説明します。

于 2013-12-03T20:35:17.563 に答える
0

この回線コード

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

スキャンされたバイト数が 1 文字よりも大きい場合は、正確に 2 バイト (そのうちの 1 つが-terminatormsgとして常に使用されます)を提供するため、惨めに失敗します。0それ以上フィードすると、 の範囲外に書き込み、msg未定義の動作を引き起こします。

これを修正するには、少なくとも 255 文字を提供してください。

char msg[256] = "";
do{
  scanf("%255s",msg);   
于 2013-12-03T20:07:35.607 に答える
0

通常、tcpdump/wiresharkを使用して、Rpi がどのパケットを認識しstraceているかを確認し、プログラムが何を行っているかを確認する必要があります。接続が機能しないことがあるという私の最初の推測は、パケットの損失です。有線 LAN (イーサネット) を使用することで、この可能性を排除できます。

しかし、あなたが使用しているサンプル サーバー コードはかなり悪い例です。一度に 1 つのクライアント接続のみを受け入れたい場合でも、サーバーはリモート メッセージに対してブロッキング待機を使用しないでください。ノンブロッキング I/Oselectまたはの使用について読んpollで、これらを使用した例を見てください。また、 についてもSO_REUSEADDRお読みください。おそらく、サーバーにも必要です。

于 2013-12-03T19:41:40.607 に答える