Saturday, July 26, 2014

Winsock TCP Client in C++

Recently I've been looking at a server-client software architecture for machine learning.  A lot of really great machine learning and mathematical modeling toolboxes are now written in Python. However, if you're working directly with a sensor-based system, your sensor capture code is often written in C/C++.

Yes, you could write a wrapper for that code, but it can be a slow and redundant process.

Instead, you can set up a Python TCP server like so , even on the localhost on the same machine, send a JSON structure with your feature vector, do your heavy number-crunching in Python, and send the result back to the C++ client.

There's a lot of documentation out there for TCP servers and clients on *nix in C and C++, but the documentation for Windows is less available, written in pure C, or cryptic to understand. I wanted a class that would abstract away most of the nuts and bolts.

Using the Windows Winsock documentation example, I've written a simple Winsock TCP client class in C++. This code will build with any semi-recent version of Visual Studio Express or Professional. It's really simple stuff, just easier to read.

Here's the header file, which I called TCPClient.h

#ifndef TCP_CLIENT_H
#define TCP_CLIENT_H
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <iostream>
#include <sstream>

#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")


class TCPClient{
public:
    TCPClient(); //Default constructor
    TCPClient(const char* host, const char* port); //Constructor with host-ipaddress, port number
    TCPClient(std::string host, std::string port); //Constructor with host-ipaddress, port number
    TCPClient(const char* host, const char* port, const int buffer_size); //Constructor with host-ipaddress, port number, buffersize [rarely used]
    TCPClient(std::string host, std::string port, const int buffer_size); //Constructor with host-ipaddress, port number, buffersize [rarely used]
    int init(); //initialization, called by constructors
    ~TCPClient(); //clean up winsock environment
    bool open(); //open connection
    bool open(const char* host, const char* port); //open connection with host-ipaddress, port number
    bool open(std::string host, std::string port); //open connection with host-ipaddress, port number
    int send_buf(const char* sendbuf);    //send a message. Returns the number of bytes successfully sent. Returns -1 if failed.
    int send_buf(std::string sendbuffer); //send a message. Returns the number of bytes successfully sent. Returns -1 if failed.
    int receive(std::string& recvbuffer); //receive a message. Returns the number of bytes received.
    bool shutdown_send(); //shutdown the outgoing connection
    bool close(); //close the socket    
private:
    int resolve(); //used internally, resolving host. Returns -1 if failed.
    int connect_to(); //used internally, actually connect. Returns -1 if failed.

    int _buffer_size;    //buffer size in bytes
    int iResult;        //errorcodes from Winsock

    WSADATA wsaData;   //Winsock stuff
    SOCKET ConnectSocket; //Socket
    struct addrinfo *result, *ptr, hints; //Winsock stuff

    std::string host; //host ipaddress
    std::string port; //port number
    bool _connection_open; // Is the connection open?
    bool _ipset; //Did we initialize with host and port number?
    bool _shutdown_sent; //Have we sent a shutdown message to the host?
};

#endif

Here's the class implementation. Again, really simple stuff.

#include "TCPClient.h"

int TCPClient::init(){
    ConnectSocket = INVALID_SOCKET;
    result=NULL;
    ptr=NULL;

    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (iResult != 0) {
        std::cerr<<"WSAStartup failed with error: "<<iResult<<"\n";
        return -1;
    }
    ZeroMemory( &hints, sizeof(hints) );
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    this->_buffer_size=4096;
    this->_shutdown_sent=false;
    return 0;
}
TCPClient::TCPClient(){
    this->_ipset=false;
    if (init()<0){
        std::cerr<<"Error initializing WinSock\n";
        exit(1);
    }
}
TCPClient::TCPClient(const char* host, const char* port){
    this->host=host;
    this->port=port;
    this->_ipset=true;
    if (init()<0){
        std::cerr<<"Error initializing WinSock\n";
        exit(1);
    }
}
TCPClient::TCPClient(std::string host, std::string port){
    this->host=host;
    this->port=port;
    this->_ipset=true;
    if (init()<0){
        std::cerr<<"Error initializing WinSock\n";
        exit(1);
    }
}
TCPClient::TCPClient(const char* host, const char* port, const int buffer_size){
    this->host=host;
    this->port=port;
    this->_ipset=true;
    this->_buffer_size=buffer_size;
    if (init()<0){
        std::cerr<<"Error initializing WinSock\n";
        exit(1);
    }
}
TCPClient::TCPClient(std::string host, std::string port, const int buffer_size){
    this->host=host;
    this->port=port;
    this->_ipset=true;
    this->_buffer_size=buffer_size;
    if (init()<0){
        std::cerr<<"Error initializing WinSock\n";
        exit(1);
    }
}
TCPClient::~TCPClient(){
    WSACleanup();
}

int TCPClient::resolve(){
    // Resolve the server address and port
    iResult = getaddrinfo(host.c_str(), port.c_str(), &hints, &result);
    if ( iResult != 0 ) {
        std::cerr<<"getaddrinfo failed with error: "<<iResult<<"\n";
        WSACleanup();
        return -1;
    }
    return 0;
}
int TCPClient::connect_to(){
    // Attempt to connect to an address until one succeeds
    for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) {
        // Create a SOCKET for connecting to server
        ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, 
            ptr->ai_protocol);
        if (ConnectSocket == INVALID_SOCKET) {           
            std::cerr<<"socket failed with error: "<<WSAGetLastError()<<"\n";
            WSACleanup();
            return -1;
        }
        // Connect to server.
        iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }
    freeaddrinfo(result);
    if (ConnectSocket == INVALID_SOCKET) {
        std::cerr<<"Unable to connect to server!\n";
        WSACleanup();
        return -1;
    }
    return 0;
}

bool TCPClient::open(){
    if (this->_ipset==false)
        return false;
    if (resolve()<0)
        return false;
    if (connect_to()<0)
        return false;
    return true;
}
bool TCPClient::open(const char* host, const char* port){
    this->host=host;
    this->port=port;
    this->_ipset=true;
    this->_connection_open=this->open();
    return _connection_open;
}
bool TCPClient::open(std::string host, std::string port){
    this->host=host;
    this->port=port;
    this->_ipset=true;
    this->_connection_open=this->open();
    return _connection_open;
}
int TCPClient::send_buf(const char* sendbuf){
    if (_connection_open==false)
        return -1;
    // Send an initial buffer

    iResult = send( ConnectSocket, sendbuf, (int)strlen(sendbuf), 0 );
    if (iResult == SOCKET_ERROR) {
        std::cerr<<"socket failed with error: "<<WSAGetLastError()<<"\n";    
        WSACleanup();
        return -1;
    }   
    return iResult;
}
int TCPClient::send_buf(std::string sendbuffer){
    
    int length=sendbuffer.length();
    if (_connection_open==false)
        return -1;
    // Send an initial buffer
    iResult = send( ConnectSocket, sendbuffer.c_str(), length, 0 );

    if (iResult == SOCKET_ERROR) {
        std::cerr<<"socket failed with error: "<<WSAGetLastError()<<"\n";
        this->close();
        WSACleanup();
        return -1;
    }   
    return iResult;
}
int TCPClient::receive(std::string& recvbuffer){
    // Receive up to _buffer_size bytes
    recvbuffer.clear();
    int total=0;
    char* recvbuf=new char[this->_buffer_size];    
    iResult = recv(ConnectSocket, recvbuf, _buffer_size, 0);
        if ( iResult > 0 ){            
            std::string received_str=recvbuf;
            received_str.resize(iResult);
            recvbuffer+=received_str;
            total+=iResult;            
        }
        else if ( iResult == 0 ){
            std::cout<<"\nConnection closed\n";            
        }
        else            
            std::cerr<<"recv failed with error: "<<WSAGetLastError()<<"\n";
   
    delete [] recvbuf;
    return total;
}
bool TCPClient::shutdown_send(){
     // shutdown the connection since no more data will be sent
    iResult = shutdown(ConnectSocket, SD_SEND);    
    if (iResult == SOCKET_ERROR) {
       std::cerr<<"shutdown failed with error: "<<WSAGetLastError()<<"\n";       
        this->close();
        WSACleanup();
        return false;
    }
    this->_shutdown_sent=true;
    return true;
}
bool TCPClient::close(){
    if (!_shutdown_sent)
        shutdown_send();        
     // cleanup
    closesocket(ConnectSocket);
    return true;
}

And finally, this is the main function, which shows how to invoke the class. I had a very simple TCP server written in Python, running on a different machine on the same local network.

The main program simple gets your keyboard input and sends it to the server. If you input the string "exit", the program exits cleanly.


int main (int argc, char* argv[]){
    TCPClient myClient("192.168.1.2","5000");    
    if (!myClient.open()){
        char a;
        std::cout<<"Enter any key to exit\n";
        std::cin>>a;
        return 0;
    }

    while (true){        
        std::string input; 
        std::cout<<"Enter something to send to the server\n";
        getline(std::cin,input);
        if (!input.compare("exit")){
            myClient.close();
            break;
        }
        if (myClient.send_buf(input)<0)    
            break;    
        std::string received;
        if    (myClient.receive(received)<0)
            break;    
        std::cout<<"Received:\n"<<received<<"\n\n";            
    }
    return 0;
}

Much easier to read and understand, than the original code on the Microsoft documentation site. Cheers!

Friday, July 11, 2014

El Nino, California Drought

NOAA performs a vital a national and international service in monitoring ocean and atmospheric conditions, recognizing patterns, and classifying and predicting significant climate events.

One of the somewhat-well-understood phenomena is the El Nino Southern Oscillation cycle. Characterized by warmer-than-average ocean temperatures across the Pacific Ocean, and a weakening or reversal of the prevailing Pacific Trade winds, El Nino years tend to bring unusual weather and some extreme weather events. In past El Nino years, we've seen unusual shifts in precipitation, including drought in some places, flooding and even snow in others.

All this is of interest to the drought-stricken American West and Southwest, because strong El Nino years tend to correlate with above-average rainfall in these regions. NOAA maintains a public communication, including monthly updates here. Currently, we are in an El Nino advisory state. A more detailed document, detailing various observations and methodologies is here.

Overall, NOAA predicts the probability of an El Nino by fall or winter at about 80%, but the El Nino is likely going to be "weak to moderate'.




I really enjoyed viewing the satellite imagery, and temperature time series in the .pdf document. I also enjoyed the numerous temperature prediction models, split into "Statistical" and "Dynamical" models. They also included an ensemble prediction. Good stuff.

Thursday, June 26, 2014

Conferences

Last week, I attended the IEEE Intelligent Vehicles Symposium, hosted this year in Dearborn, Michigan. It was nice to run into old colleagues from UCSD, as well as former collaborators from places like Toyota, and researchers from around the world.

I've always enjoyed attending conferences, as they provide a forum for exchanging new ideas and learning about new developments. In our own labs and workplaces, we can get stuck, recycling the same ideas and approaches, trapped in our echo chambers. At a conference, we can learn from each other.

There was a lot of work presented on autonomous driving. Honestly, the topic is not very exciting anymore. Autonomous driving has been done reliably by many groups, and has really transitioned from a research problem into an engineering problem. Now car companies are figuring out how to bring autonomous driving to the market and make profits from it.

In particular, a former collaborator at UCSD, Eshed Ohn-Bar, presented some of his work on hand-tracking in the vehicle, which he's had quite a bit of experience with. He also clued me in to some new work from Pietro Perona's group on object detection. It turns out that Serge Belongie, who sat on my PhD committee, is also a co-author on the paper. Good stuff.

There was a time when I was sick of doing computer vision. I took a break from it for a little while, working with some other sensors, and learning about some other areas. My plan is to get back into some computer vision on a work project later this year.


Saturday, May 10, 2014

Springtime is here!

Springtime, baby. Sunshine, warmth, hiking, camping, grilling, gardening, and more outdoor activities are on the schedule. To be fair, the Bay area doesn't get much of a winter, just a couple of months of rainy weather, which frankly, are integral to the eco-system.

That said, the transition to more a real winter, moving from San Diego to the Bay, was not insignificant. Glad to be in the sunshine for the next few months.

Saturday, May 3, 2014

Reversing C-Style Strings With Pointers

It's a common interview question to ask an applicant to reverse a c-style string, without using string utility functions. In practice, this is something you almost never do, as modern programming languages have nice, safe string classes and interfaces, most likely complete with their own string::reverse() methods.

However, the test has some value. Depending on how the applicant approaches the task, you can gauge his comfort level with pointers. You can get a feel for the applicant's efficiency with memory and operations. Do they reverse the string in place, or allocate new memory?

In particular, swapping the values of two variables, is an interesting sub-test. Can the applicant do this correctly? How do they choose to do it? They can do it in a separate function, with pointers, with references, etc.

Finally, testing. Does the applicant simply write an answer and say "tada"? Or do they try to walk through the code and test what it does, maybe catching mistakes or corner-cases?

As long as the applicant can correctly complete the task, there are no wrong answers. This test really elucidates their style and thought process.

Even though a person might scoff at such a "trivial" test that "doesn't prove anything," it turns out that the test is not so trivial for most programmers. Notably, most applicants cannot correctly complete this task the first time.

Now to the code:

1:  void reverse_str(char* str){  
2:       //Receive a c-style string, meaning a pointer to an array of type char,  
3:       //Reverse in place  
4:       char* lower=str;  
5:       char* upper=lower;            
6:       while (*upper!='\0')     //Searching for the end of a null-terminated string       
7:            upper++;       
8:       upper--;          //Decrement it; we want the last non-null element  
9:       while (upper>lower){       
10:            char temp=*lower;  
11:            *lower=*upper;  
12:            *upper=temp;                                          
13:            upper--;  
14:            lower++;  
15:       }  
16:  }  
In this example, I've used pointers. To find the length of the string, I iterate the pointer [upper] until I find the null-character. I then decrement this pointer, to the last non-null character.

Then, I swap the values of the lower and upper pointers, incrementing the lower and decrementing the upper, until they meet in the middle. And that's it.

If I wanted to be a little more elegant with my swapping, I might write a separate function to swap the values of the pointers. How might I do that?

1:  template<class T>  
2:  void swap(T* a, T* b){  
3:       //Swap the values of two pointers  
4:       T temp=*a;  
5:       *a=*b;  
6:       *b=temp;  
7:  }  

In this case, I've chosen to swap using pointers. So, lines 10-12 in reverse_str() would change to:
1:  swap(lower,upper);  

One other option could be to swap using references. It's actually pretty important that an applicant understands references in C++, as this is a common and integral feature of the language. [Maybe I'll do a follow-up entry on this at some point]. Here's a swap with references:

1:  template<class T>  
2:  void swap(T& a, T& b){  
3:       //Swap the values of two variables  
4:       T temp=a;  
5:       a=b;  
6:       b=temp;  
7:  }  

In this case, lines 10-12 of reverse_str() would need the pointers to be dereferenced:

1:  swap(*lower,*upper);  

So yeah, there you go. There are myriads of other ways to approach this problem. For such a seemingly simple task, you get a lot of mileage and information about a software engineer.

The Only Democrat in CA Who Won't Blow the Budget

This year, amidst a tax increase and an economic recovery, after years of budget cuts and massive deficits, the state of California is turning a nice surplus. Most Democrats in Sacramento want to immediately establish new permanent programs.

 Perhaps in some cases, they argue for reinstating some of the social welfare programs that were cut during the Great Recession. In other cases, they'd like to establish brand new programs, because if there's some extra money this year, why no establish new spending obligations with no guarantee of budget surpluses next year?

Need we remind them that the voter-approved tax increase was temporary, and that economic booms [Silicon Valley, Wall Street] are also temporary?

 Jerry Brown is proposing an initiative that would set aside a rainy day fund, if the fiscal year's capital gains tax comprises beyond a certain percentage of overall tax revenue. He's the most responsible politician in Sacramento. 

Thursday, May 1, 2014

Binary Search With Pointers, C++

Today I'm posting my version of binary search, using pointers in C++. Binary search is a fundamental algorithm, which searches for an element in a sorted array. It is considered the mother of all divide-and-conquer algorithms, and executes in log(N) time, where N is the size of the array. The log is taken with respect to base 2 [binary].

Implementing Binary Search with pointers is a nice exercise that brings together a few concepts.

1. While binary search is an important algorithm from a conceptual standpoint, it's also highly practical and has come up in my research and work. Consider captured data that is indexed by its timestamp, e.g.

                timestamp :  value 1, value 2,....value N
             
The most efficient way to search for the values at a particular timestamp is binary search. In reality, we may want the nearest timestamp to a given reference, but that's a minor adjustment to the algorithm.

2. Implementing binary search with pointers helps to reinforce understanding of pointers, a concept which tends to trip up many C++ programmers.

3. Despite the fact that binary search is a fundamental algorithm, very few job applicants are able to code it correctly on a whiteboard. In short, it's good practice.

In our example, we initialize an array with random elements, then sort it. First we demonstrate how we find an element in the array. Second, we demonstrate how the function behaves when we search for an unknown element. Now, to the code:

 #include <iostream> //For std::cout  
 #include <algorithm> //For std::sort  
 #include <stdlib.h> //For srand, rand   
 #include <time.h>  //For time   
   
 template<class T> //template class means we can accept arguments of different classes: int, float, double, etc.  
 int binarysearch(T* arr /*pointer to beginning of array*/, T* low, T* high, T searchkey /*seeking this value in array*/){  
      int index=-1; //Default value, if we don't find searchkey  
      if (*low>*high) //Double-checking that the array's actually sorted  
           return index;  
      if (low>high) //At this point, we've searched the whole array  
           return index;  
   
      T* mid=(high-low)/2+low; //The array index midway between high and low  
      if (*mid==searchkey)   //We've found the searchkey  
           return mid-arr;  
      if (searchkey<*mid)  
           return binarysearch(arr,low,mid-1,searchkey); //Recursive call to search the lower half of the array  
      else if (searchkey>*mid)  
           return binarysearch(arr,mid+1,high,searchkey); //Recursive call to search the upper half of the array  
   
      return index;  
 }  
   
 int main(int argc, char* argv[]){  
      //Using srand to initialize rand() with time()  
      srand (time(NULL));  
   
      //Size of our array will be 15  
      int size=15;   
      int* arr=new int [size]; //Note that arr is the array, and is also a pointer. It points to a memory block of 15 integers.  
      for (int i=0; i<size; i++){            
           arr[i]=rand()%100; //Randomly initialize the elements of the array, integers between 0 and 100  
           std::cout<<arr[i]<<",";  
      }  
      std::cout<<"\n";                 
      std::sort(arr,arr+size); //Sort the array  
      for (int i=0; i<size; i++)  
           std::cout<<arr[i]<<",";  
      std::cout<<"\n";  
   
      //Finding element in array.   
      int to_find=arr[rand()%size]; //Choosing an element at random from the array to search using binary search  
      int found_index=binarysearch(arr,arr,arr+size-1,to_find);  
      if (found_index>-1)  
           std::cout<<"Found "<<to_find<<" at location "<<found_index<<"\n\n";  
      else  
           std::cout<<"Couldn't find "<<to_find<<" in array\n\n";  
   
      //Finding unknown element  
      to_find=100; //We initialized the array with elements strictly smaller than 100  
      found_index=binarysearch(arr,arr,arr+size-1,to_find);  
      if (found_index>-1)  
           std::cout<<"Found "<<to_find<<" at location "<<found_index<<"\n";  
      else  
           std::cout<<"Couldn't find "<<to_find<<" in array\n";  
   
      delete [] arr; //Clean up dynamically allocated memory  
      return 0;  
 }