如果您不知道有多少数据通过,如何接收套接字信息?

gri*_*ett 3 python arduino beagleboard

我正在开发一个基于Python的GUI界面,与运行Python的机器人和Arduino Mega作为电机控制器和传感器控制器进行交互.

最初,我打算使用远程桌面从机器人加载我的GUI.由于远程桌面,这变得非常慢.我决定服务器和客户端会更好.

我在我的Arduino上运行了一个草图,它捕获了马达命令并执行它们.它还等待"Ping"命令通过,此时应检查三个不同位置的超声波传感器,然后将此信息写回服务器,服务器应捕获此数据,然后将其传递给客户端GUI.我已经完成了大部分工作,但我似乎无法将数据从服务器返回给客户端.我曾经想过一个简单的"client.recv()"会实现这一点,但事实并非如此.

如果我不确切知道有多少数据会回来,我怎样才能收到这些数据?

Arduino将数据发送为"dist1,dist2,dist3 \n".

这是我的Arduino代码:

#include <LiquidCrystal.h>
#include <Ping.h>
#include <Servo.h>
// Parallax Ping Unit for distance sensing.
Ping sonic(22);

// Setup LCD pins.
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

// Servo for Ping unit sweeping.
Servo  PingServo;

// Setup Ping distance setting variables.
int pingDisCent;
int pingDisLeft;
int pingDisRight;

// Variable to keep commands in.
char MsgRcvd;

// Motor setup information.

int LF[] = {23,24};
int LR[] = {25,26};
int RF[] = {27,28};
int RR[] = {29,30};

// Set Debugging here
// 1 - Debug on - Motors don't turn when commands are sent.
// 0 - Debug off - Motors turn when commands are sent.
int debug = 1;

//Variables for speed
int SpdPin = 22;
int Speed = 255;

void setup()
{
  Serial.begin(9600);  // start serial communications

  // Setup motors for output.
  int i;
  for(i = 0; i < 2; i++){
    pinMode(LF[i], OUTPUT);
    pinMode(LR[i], OUTPUT);
    pinMode(RF[i], OUTPUT);
    pinMode(RR[i], OUTPUT);
  }

  // Setup servo to sweep.
  PingServo.attach(6);
  PingServo.write(90);

  // Set up the LCD's number of rows and columns:
  lcd.begin(16, 2);

  // Print a message to the LCD.
  lcd.print("Waiting...");

  // Setup speed pin.
  pinMode(SpdPin, OUTPUT);
}

void loop()
{
  if (Serial.available() > 0)    //Check to see if a command is available.
  {
    MsgRcvd = Serial.read();    // If a command is there, see what it is.
    switch (MsgRcvd)
    {
      case '0':
        Stop();
        break;
      case '1':
        MoveForward();
        break;
      case '2':
        MoveLeft();
        break;
      case '3':
        MoveRight();
        break;
      case '4':
        MoveBackward();
        break;
      case '~':
        active_ir();
        break;
      case 'M':                    // Check to see if we have a connection from the GUI - if so spit out information to the LCD.
        lcd.clear();
        lcd.print("Connected");
        lcd.setCursor(0,1);
        lcd.print("waiting..");
        break;
      case 'D':
        lcd.setCursor(0,1);
        lcd.print("Disconnected"); // Client disconnected - spit out a disconnect to the LCD.
        break;
    }
  }
  delay(100);
}

//  ===================================
//  =====    Ping Ultrasonic      =====
//  ===================================
void active_ir()
{
  // Read to the right.
  PingServo.write(30);
  delay(300);
  pingDisRight = sonic.inch();
  delay(500);

  // Read to the front.
  PingServo.write(90);
  delay(300);
  pingDisCent = sonic.inch();
  delay(500);
  //  Read to the left.
  PingServo.write(150);
  delay(300);
  pingDisLeft = sonic.inch();
  delay(500);
  Serial.print(pingDisLeft);
  Serial.print(',');
  Serial.print(pingDisCent);
  Serial.print(',');
  Serial.println(pingDisRight);
  return;
}



//  ==========================================
//  ======        MOTOR CONTROL      =========
//  ==========================================

void MoveForward()
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Forward");
  if (debug == 0){

    digitalWrite(LF[0], HIGH);
    digitalWrite(LF[1], LOW);
    digitalWrite(LR[0], HIGH);
    digitalWrite(LR[1], LOW);
    digitalWrite(RF[0], HIGH);
    digitalWrite(RF[1], LOW);
    digitalWrite(RR[0], HIGH);
    digitalWrite(RR[1], LOW);
  }
}

void MoveBackward()
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Reverse");
  if (debug == 0){
    analogWrite(SpdPin, Speed);
    digitalWrite(LF[0], LOW);
    digitalWrite(LF[1], HIGH);
    digitalWrite(LR[0], LOW);
    digitalWrite(LR[1], HIGH);
    digitalWrite(RF[0], LOW);
    digitalWrite(RF[1], HIGH);
    digitalWrite(RR[0], LOW);
    digitalWrite(RR[1], HIGH);
  }
}

void MoveLeft()
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Left");
  if (debug == 0){
    analogWrite(SpdPin, Speed);
    digitalWrite(LF[0], LOW);
    digitalWrite(LF[1], HIGH);
    digitalWrite(LR[0], LOW);
    digitalWrite(LR[1], HIGH);
    digitalWrite(RF[0], HIGH);
    digitalWrite(RF[1], LOW);
    digitalWrite(RR[0], HIGH);
    digitalWrite(RR[1], LOW);
  }
}

void MoveRight()
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Right");
  if (debug == 0) {
    analogWrite(SpdPin, Speed);
    digitalWrite(LF[0], HIGH);
    digitalWrite(LF[1], LOW);
    digitalWrite(LR[0], HIGH);
    digitalWrite(LR[1], LOW);
    digitalWrite(RF[0], LOW);
    digitalWrite(RF[1], HIGH);
    digitalWrite(RR[0], LOW);
    digitalWrite(RR[1], HIGH);
  }
}

void Stop()
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Stopping");
  if (debug == 0){
    digitalWrite(LF[0], LOW);
    digitalWrite(LF[1], LOW);
    digitalWrite(LR[0], LOW);
    digitalWrite(LR[1], LOW);
    digitalWrite(RF[0], LOW);
    digitalWrite(RF[1], LOW);
    digitalWrite(RR[0], LOW);
    digitalWrite(RR[1], LOW);
  }
}
Run Code Online (Sandbox Code Playgroud)

这是我的Python服务器代码:

import serial
import socket

Serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Serv.bind(('', 9000))
Serv.listen(1)
print "Listening on TCP 9000"
motor = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
print "Connected to Motor Controller: /dev/ttyUSB0"

while(1):
    print "Waiting For Connection..."
    connection, addr = Serv.accept()
    connection.setblocking(0)
    print "Connected by", addr[0]
    while(1):
        try:
            Servdata = connection.recv(1)
            break
        except:
            pass
    if (Servdata == 'M'):
        print "Entering Manual Mode"
        motor.write(Servdata)
        while(Servdata != 'X'):
            Servdata = '9'
            try:
                Servdata = connection.recv(1)
            except:
                pass
            if Servdata == 'X':
                print "Exiting"
                break
            if Servdata == '0':
                print "Stopping"
                motor.write(Servdata)
            if Servdata == '1':
                print "Forward"
                motor.write(Servdata)
            if Servdata == '2':
                print "Left"
                motor.write(Servdata)
            if Servdata == '3':
                print "Right"
                motor.write(Servdata)
            if Servdata == '4':
                motor.write(Servdata)
                print "Backwards"
            if Servdata == '~':
                motor.write(Servdata)
                retval = motor.readline()
                Serv.send(retval)
            else:
                pass
        motor.write('0')
        connection.close()
        print addr[0], "Closed Manual Mode"
Run Code Online (Sandbox Code Playgroud)

最后但并非最不重要的是,客户端GUI代码(这也是我认为我的问题所在......):

from socket import *
from PythonCard import model
HOST = ''
PORT = 9000
ADDR = (HOST,PORT)
BUFSIZE = 4096
Client = socket (AF_INET,SOCK_STREAM)
Client.connect((ADDR))
Client.send('M')
class MainWindow(model.Background):
    def on_SetSpdBtn_mouseClick(self, event):
        spd = self.components.SpdSpn.value
    def on_FwdBtn_mouseClick(self, event):
        spd = self.components.SpdSpn.value
        Client.send('1')
    def on_LftBtn_mouseClick(self, event):
        spd = self.components.SpdSpn.value
        Client.send('2')
    def on_RitBtn_mouseClick(self, event):
        spd = self.components.SpdSpn.value
        Client.send('3')
    def on_RevBtn_mouseClick(self, event):
        spd = self.components.SpdSpn.value
        Client.send('4')
    def on_StpBtn_mouseClick(self, event):
        spd = self.components.SpdSpn.value
        Client.send('0')
    def on_GetPing_mouseClick(self, event):
        Client.send('~')
        retval = Client.recv()
        ping_data = retval.strip() # Strip out the newline, if you read an entire line.
        split_data = ping_data.split(',')
        L_Ping = split_data[0]
        R_Ping = split_data[1]
        self.components.PingLeft.text = str(L_Ping)
        self.components.PingRight.text = str(R_Ping)
app = model.Application(MainWindow)
app.MainLoop()
Run Code Online (Sandbox Code Playgroud)

sar*_*old 5

我想我在这段代码中发现了三个问题; 第一个是浪费,第二个可能是你今天来到这里的原因,第三个是为什么你认为你今天进来的原因.:)

忙着等待

此代码正忙于等待连接上的数据进入:

connection.setblocking(0)
print "Connected by", addr[0]
while(1):
    try:
        Servdata = connection.recv(1)
        break
    except:
        pass
Run Code Online (Sandbox Code Playgroud)

再来一次:

    while(Servdata != 'X'):
        Servdata = '9'
        try:
            Servdata = connection.recv(1)
        except:
            pass
        # ...
        else:
            pass
Run Code Online (Sandbox Code Playgroud)

这会像疯狂一样燃烧CPU周期; 更好的希望你不是靠电池供电.它也不会给任何东西; 你不妨称之为封锁 recv().让CPU在等待下一个输入字节时进入休眠状态.(如果你实际上对任何事情使用非阻塞,那么繁忙的等待会更有意义,但你不是.如果你想限制服务器阻止输入的时间,总会有settimeout().但不要盲目也可以使用,因为只需切换到阻塞,这段代码就会受益最多recv().)

不向客户端发送数据

        if Servdata == '~':
            motor.write(Servdata)
            retval = motor.readline()
            Serv.send(retval)
Run Code Online (Sandbox Code Playgroud)

我认为这段代码还没有被执行:) Serv没有连接任何东西,它是一个监听套接字.你可能connection.send(retval)在这里意味着; 我在服务器中找不到任何实际将数据发送到客户端的其他行,所以可能这应该是它.

一次全部

客户端中的这段代码有点脆弱,但可能永远不会破坏:

def on_GetPing_mouseClick(self, event):
    Client.send('~')
    retval = Client.recv()
    ping_data = retval.strip() # strip out the newline, if you read an entire line
    split_data = ping_data.split(',')
    L_Ping = split_data[0]
    R_Ping = split_data[1]
    self.components.PingLeft.text = str(L_Ping)
    self.components.PingRight.text = str(R_Ping)
Run Code Online (Sandbox Code Playgroud)

此代码假定recv()调用将返回一个协议消息.TCP流不能以这种方式工作,对等端可以随意发送任何大小的传出数据.(TCP/IP堆栈始终将多个应用程序级别的消息组合成一个TCP数据包.它们还发送小于请求的数据包,以避免碎片.)

更安全的是使用从远程对等方接收的内容填充队列,然后解析队列中的命令/消息.您可能在队列中找到十个命令,您可能只找到命令的一部分 - 但您的代码需要准备好在队列上推送部分消息,并在队列可用时消耗完整的消息.

这是一项额外的工作,但需要在不太理想的情况下安全操作.您可能永远不会在局域网上遇到问题,但在无线网络或通过大型网络路由时会遇到问题.