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 一样,下次我们启动程序时,一切都会正常工作,有时会运行两到三遍,然后它就会停止连接。

我已经在谷歌上搜索过类似的问题,但我没有找到类似的问题,所以我现在直接求助于你。

这是我们在树莓派上运行的服务器的代码,它也用作网络热点:

#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

通常,您应该使用tcpdump/wireshark来查看您的 Rpi 看到了哪些数据包,并strace查看您的程序做了什么。我对您的连接有时无法正常工作的第一个猜测是数据包丢失。通过使用有线 LAN(以太网),您可以排除这种可能性。

但是您使用的示例服务器代码是一个相当糟糕的示例。即使您一次只想接受一个客户端连接,您的服务器也不应该对任何远程消息使用阻塞等待。您应该阅读有关使用非阻塞 I/Oselect或的信息poll,并查看使用这些的示例。另外,请阅读关于SO_REUSEADDR,您的服务器中可能也需要那个。

于 2013-12-03T19:41:40.607 回答
0

此行代码

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

如果扫描的字节数大于 1 个字符,则会惨遭失败,因为msg恰好提供了两个字节(其中一个始终用作0- 终止符)。喂食更多会超出范围,msg这样做会引发未定义的行为。

要解决此问题,请至少提供至少 255 个字符:

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

除非您实际上真正想学习的是低级 berkeley 套接字操作,否则我建议您查看libevent或类似的库。

主循环的结构有点不寻常。显然,您一次只能处理一个连接,并且您无法很好地处理在为之前的连接提供服务时发生的任何连接尝试。

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

绑定可能会失败,例如,如果另一个进程最近打开了套接字并且操作系统还没有完成清理端口的使用。您可以更改此行为,但仍应从die.net 的绑定手册页中进行检查

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 回答