added/improved comments and added support for skipping either part
diff --git a/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.cpp b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.cpp
index 66a6bf9..ff1b77a 100644
--- a/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.cpp
+++ b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.cpp
@@ -90,25 +90,33 @@
 
   // This outer loop is so that it will retry after encountering any errors.
   while (true) {
-    CreateReceiveSocket();
-    if (StatusIsFatal()) return;
-    CreateSendSocket();
-    if (StatusIsFatal()) return;
-
-    send_task_.Start(reinterpret_cast<uintptr_t>(this));
-
-    if (!FillinInAddr(sender_address_, &expected_sender_address_)) return;
-
-    while (!StatusIsFatal()) {
-      if ((Timer::GetPPCTimestamp() - last_received_timestamp_) > kDisableTime) {
-        StopOutputs();
-      }
-      ReceivePacket();
+    if (sender_address_ != NULL) {
+      CreateReceiveSocket();
+      if (!FillinInAddr(sender_address_, &expected_sender_address_)) return;
     }
-    StopOutputs();
 
-    send_task_.Stop();
-    ClearError();
+    if (receiver_address_ != NULL) {
+      CreateSendSocket();
+    }
+
+    if (sender_address_ != NULL) {
+      if (receiver_address_ != NULL) {
+        send_task_.Start(reinterpret_cast<uintptr_t>(this));
+      }
+
+      while (!StatusIsFatal()) {
+        if ((Timer::GetPPCTimestamp() - last_received_timestamp_) >
+            kDisableTime) {
+          StopOutputs();
+        }
+        ReceivePacket();
+      }
+      StopOutputs();
+    } else {
+      SendLoop();
+    }
+
+    Cleanup();
   }
 }
 
@@ -134,13 +142,36 @@
   }
 
   // Can't do anything intelligent with solenoids. Turning them off can be just
-  // as dangerous as leaving them on.
+  // as dangerous as leaving them on, so just leave them alone.
 
   // We took care of it, so we don't want the watchdog to permanently disable
   // us.
   m_watchdog.Feed();
 }
 
+void NetworkRobot::Cleanup() {
+  send_task_.Stop();
+
+  if (receive_socket_ != -1) {
+    if (close(receive_socket_) == ERROR) {
+      char buf[64];
+      snprintf(buf, sizeof(buf), "close(%d)", receive_socket_);
+      wpi_setErrnoErrorWithContext(buf);
+    }
+    receive_socket_ = -1;
+  }
+  if (send_socket_ != -1) {
+    if (close(send_socket_) == ERROR) {
+      char buf[64];
+      snprintf(buf, sizeof(buf), "close(%d)", send_socket_);
+      wpi_setErrnoErrorWithContext(buf);
+    }
+    send_socket_ = -1;
+  }
+
+  ClearError();
+}
+
 bool NetworkRobot::WaitForData() {
   assert(kDisableTime < 1.0);
 
diff --git a/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.h b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.h
index b17bef2..4511d2b 100644
--- a/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.h
+++ b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.h
@@ -36,6 +36,8 @@
 class NetworkRobot : public RobotBase, public ErrorBase {
  protected:
   // Does not take ownership of *sender_address or *receiver_address.
+  // A NULL for either address means to not do anything with that part (sending
+  // or receiving).
   NetworkRobot(UINT16 receive_port, const char *sender_address,
                UINT16 send_port, const char *receiver_address);
   virtual ~NetworkRobot();
@@ -67,6 +69,10 @@
   // error will have already been recorded.
   bool FillinInAddr(const char *const_ip, in_addr *inet_address);
 
+  // Cleans everything up after the main loop encounters an error so that it can
+  // try again.
+  void Cleanup();
+
   // Waits for receive_socket_ to become readable for up to kDisableTime.
   // Returns whether or not there is readable data available.
   bool WaitForData();
@@ -74,7 +80,7 @@
   // Receives a packet and calls ProcessPacket() if it's a good one.
   void ReceivePacket();
 
-  // Gets run in a separate task to take DS data and send it out.
+  // Gets run in its own task to take DS data and send it out.
   void SendLoop();
   static void StaticSendLoop(void *self) {
     static_cast<NetworkRobot *>(self)->SendLoop();