//SerServ 0.2 // //A TCP/IP server that gives multiple clients //access to a serial port on the host machine //over the network. UDP packets are accepted //as input. // //This program depends on two libraries: // // pthreadWin32 posix threads (Win32 only) // qextser cross platform serial port // //History: // //0.1 03-Dec-2002 // Initial Release // //0.2 09-Dec-2002 // Eleminated locking of two mutexes at once in same thread // Made all STL vectors of objects, not pointers // Fixed memory leak in outbound_packet_handler // Added copy constructor to Packet_Msg // Added assignment operator to Packet_Msg // Added sem_new_client between net_server and net_reader // Put code for adding a client in one place #ifdef WIN32 #define FD_SETSIZE 65535 #endif //WIN32 #ifndef WIN32 #include #include #include #include #include #include #include #endif //!WIN32 #include "qextserialport.h" #include #include #include #include using namespace std; //dirty getch() #include ////////////////////////////// //Definitions #define SERSERV_SERVER_PORT 4321 class Packet_Msg { public: unsigned int source; unsigned long size; char* data; Packet_Msg(); Packet_Msg(const Packet_Msg&); ~Packet_Msg(); Packet_Msg& operator = (const Packet_Msg&); }; Packet_Msg::Packet_Msg() { source = 0; size = 0; data = NULL; } Packet_Msg::Packet_Msg(const Packet_Msg& tmp_pkt) { *this = tmp_pkt; } Packet_Msg::~Packet_Msg() { if (data) { delete[] data; } } Packet_Msg& Packet_Msg::operator = (const Packet_Msg& tmp_msg) { source = tmp_msg.source; size = tmp_msg.size; if (size) { data = new char[size]; memcpy(data, tmp_msg.data, size); } return *this; } //Definitions ////////////////////////////// ////////////////////////////// //Global variables sem_t sem_new_client; sem_t sem_from_net; sem_t sem_to_net; sem_t sem_from_ser; sem_t sem_to_ser; //new clients waiting to be added pthread_mutex_t mutex_list_new_client; vector < SOCKET > list_new_client; //all active clients pthread_mutex_t mutex_active_clients; fd_set active_clients; //packets already read from the network pthread_mutex_t mutex_list_inbound_packet; vector < Packet_Msg > list_inbound_packet; //packets waiting to be sent on network pthread_mutex_t mutex_list_outbound_packet; vector < Packet_Msg > list_outbound_packet; //data ready to write to the serial port pthread_mutex_t mutex_list_to_ser; vector < string > list_to_ser; //data read from the serial port pthread_mutex_t mutex_list_from_ser; vector < string > list_from_ser; //Global variables ////////////////////////////// //Acts on data already read from network void* thread_func_inbound_packet_handler(void*) { printf("inbound_packet_handler:\t\tThread started.\n"); vector < Packet_Msg > list_private; int continue_flag = 1; while(continue_flag) { //wait for data from the network sem_wait(&sem_from_net); //fill list_private with list_inbound_packet if (pthread_mutex_lock(&mutex_list_inbound_packet) == 0) { for (unsigned int x=0; x list_private; int continue_flag = 1; while(continue_flag) { //pause for serial port activity sem_wait(&sem_from_ser); //fill list_private with list_from_ser if (pthread_mutex_lock(&mutex_list_from_ser) == 0) { for (unsigned int x=0; x private_list; timeval time_to_wait; time_to_wait.tv_sec = 2; time_to_wait.tv_usec = 0; //Create a UDP socket sockfd_server_udp = socket(AF_INET, SOCK_DGRAM, 0); //Setup the address to bind to server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = htonl(INADDR_ANY); server_address.sin_port = htons(SERSERV_SERVER_PORT); //bind if (bind(sockfd_server_udp, (struct sockaddr*) &server_address, sizeof(server_address))) { printf("net_reader: Failed on bind sockfd_server_udp.\n"); return NULL; } listen(sockfd_server_udp, FD_SETSIZE); continue_flag = 1; while(continue_flag) { //use a different fdset for select, so we //don't disrupt our good list of acitve cilents if (pthread_mutex_lock(&mutex_active_clients) == 0) { fdset_for_select = active_clients; pthread_mutex_unlock(&mutex_active_clients); } else { printf("net_reader: ERROR: \ Locking mutex_active_clients.\n"); continue_flag = 0; } //Put the UDP server into fdset_for_select FD_SET(sockfd_server_udp, &fdset_for_select); result = select(FD_SETSIZE, &fdset_for_select, NULL, NULL, &time_to_wait); // result = select(FD_SETSIZE, &fdset_for_select, NULL, NULL, NULL); //Test for select error if (result < 0) { printf("net_reader: ERROR: \ Failed on select call.\n"); continue_flag = 0; } //Test for select timeout if (result == 0) { // printf("net_reader: Select timeout.\n"); //Setup the next timeout time_to_wait.tv_sec = 1; time_to_wait.tv_usec = 0; } //Test for select activity if (result > 0) { int total_fd_active = 0; int fd = 0; int keep_testing_fd = 1; while (keep_testing_fd) { //Read the data from a pending client if (FD_ISSET(fd, &fdset_for_select)) { //get the number of characters ready for reading #ifndef WIN32 ioctl(fd, FIONREAD, &nread); #endif #ifdef WIN32 ioctlsocket(fd, FIONREAD, &nread); #endif //When nread=0, the client is gone if (nread == 0) { //Remove a client #ifndef WIN32 close(fd); #endif #ifdef WIN32 shutdown(fd, 0); closesocket(fd); #endif if (pthread_mutex_lock(&mutex_active_clients) == 0) { FD_CLR(fd, &active_clients); printf("net_reader: Client %d removed.\n", fd); pthread_mutex_unlock(&mutex_active_clients); } else { printf("net_reader: ERROR: \ Locking mutex_active_clients.\n"); continue_flag = 0; } } else { //Data available on client fd char* read_buf; read_buf = new char[nread]; #ifndef WIN32 nread = read(fd, &read_buf, nread); #endif #ifdef WIN32 nread = recv(fd, read_buf, nread, 0); #endif //Put data into list_inbound_packet if (pthread_mutex_lock(&mutex_list_inbound_packet) == 0) { //put the buffer into the list_inbound_packet Packet_Msg tmp_pkt_msg; tmp_pkt_msg.source = (unsigned int) fd; tmp_pkt_msg.size = (unsigned long) nread; if (tmp_pkt_msg.size) { tmp_pkt_msg.data = new char[nread]; memcpy(tmp_pkt_msg.data, read_buf, nread); } list_inbound_packet.push_back(tmp_pkt_msg); sem_post(&sem_from_net); pthread_mutex_unlock(&mutex_list_inbound_packet); } else { printf("net_reader: ERROR: \ Locking mutex_list_inbound_packet.\n"); continue_flag = 0; } delete[] read_buf; } } fd++; if (fd < FD_SETSIZE) { if (total_fd_active == result) { // printf("Exiting fd loop after answering all active.\n"); keep_testing_fd = 0; } } else { // printf("Exiting fd loop on end.\tResult:\t%d\n", result); keep_testing_fd = 0; } } } //Unrelated to prior select call //Add any new clients if (sem_trywait(&sem_new_client) == 0) { vector < SOCKET > list_private_clients; //fill list_private_clients with list_from_ser if (pthread_mutex_lock(&mutex_list_new_client) == 0) { for (unsigned int x=0; x list_private_pkt_out; //fill list_private_pkt_out with list_outbound_packet if (pthread_mutex_lock(&mutex_list_outbound_packet) == 0) { for (unsigned int x=0; x 0) { for (unsigned int x=0; x 0) { if (FD_ISSET(sockfd_server_tcp, &fdset_for_select)) { //Add a client sockfd_client = accept(sockfd_server_tcp, NULL, NULL); //The other threads must know about new clients if (pthread_mutex_lock(&mutex_list_new_client) == 0) { list_new_client.push_back(sockfd_client); printf("net_server: Client ready to add:\t%d.\n", sockfd_client); sem_post(&sem_new_client); pthread_mutex_unlock(&mutex_list_new_client); } else { printf("net_server: ERROR: \ Locking mutex_list_new_client\n"); continue_flag = 0; } } } } printf("net_server: Thread exiting.\n"); return NULL; } //Handles serial port read/write //Data read from serial port goes into list_from_ser //Data in list_to_ser is written to the serial port void* thread_func_ser_port(void*) { printf("ser_port:\t\t\tThread started.\n"); string tmp_str; vector < string > private_list; QextSerialPort sp("COM2"); sp.setBaudRate(BAUD4800); sp.setDataBits(DATA_8); sp.setFlowControl(FLOW_OFF); sp.setStopBits(STOP_1); sp.setParity(PAR_NONE); if (sp.open()) { int continue_flag = 1; while (continue_flag) { char tmp_char = 0; //this loop will grab each line coming in on //the serial portand put it into tmp_str tmp_str.clear(); while (tmp_char != '\n') { while (sp.bytesWaiting() < 1) { Sleep(50); } tmp_char = sp.getch(); tmp_str += tmp_char; } if (tmp_str.size() > 1) { private_list.push_back(tmp_str); } //when we get a chance, move everything out //of private_list into list_from_ser if (private_list.size()) { if (pthread_mutex_trylock(&mutex_list_from_ser) == 0) { for (unsigned int x=0; x