套接字任意连接 - 或不是

时间:2013-12-03 19:20:47

标签: c sockets raspberry-pi stream-socket-client

我正在开展大学项目,我必须将覆盆子pi连接到Android智能手机来控制2台电机。 我们是套接字编程的新手,所以我们从wikibooks上的一个例子开始,并尝试修改我们的需求。我们现在面临的问题是,服务器和客户端之间的连接是非常随意和不稳定的,有时会连接,并且在短暂断开连接后再次连接。奇怪的是(对我来说)是,在我们编辑负责连接的部分上方的代码之后:

 /* 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一样,下次我们启动程序时,everthing确实有效,有时两到三次,然后它就会停止连接。

我已经在谷歌搜索了所有类似的问题,但我还没找到相应的问题,所以我现在直接转向你。

这是我们在raspberry pi上运行的服务器的代码,它也是一个网络热点:

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

我们做错了什么想法?

提前致谢!

3 个答案:

答案 0 :(得分:0)

通常,您应该使用tcpdump / wireshark来查看Rpi看到的数据包,strace以查看您的程序执行的操作。我对你的连接有时不起作用的第一个猜测就是数据包丢失。通过使用有线LAN(以太网),您可以排除这种可能性。

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

答案 1 :(得分:0)

此行代码

char msg[] = " ";
do{
  scanf("%s",msg);   
如果扫描的字节数大于1个字符,

将失败,因为msg正好提供两个字节(其中一个字节总是用作0 - 终结符)。喂食更多会写出msg的范围,这样做会引起不明确的行为。

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

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

答案 2 :(得分:0)

除非您真正想要学习的是低级别的伯克利套接字操作,否则我建议您查看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);
}

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。

正如我上面提到的,这种方法仍然存在问题,但我不打算在这里教你一次性了解套接字所需的一切:)如果你想进一步发展你的套接字知识,你的下一站应该通过非阻塞套接字了解selectpoll,以便您可以托管多个套接字并在它们变为活动状态时为它们提供服务。