Solved

Question about socket programming.

Posted on 2010-09-09
7
477 Views
Last Modified: 2012-05-10
Hey Experts.

I'm building a multithreaded client/server chat program on Linux at work.
The server component is composed of three classes: Server, ServerDispatcher and ClientHandler.
The client component is composed of two classes: Client and Sender.
In the middle of all that is a Socket class with all the functions related to sockets.

The server component is all done but when I got to the client component, I got stuck with how I am supposed to use the file descriptor created after the connection is accepted on a Sender class I have. This class is a thread that is supposed to read keyboard input from the user and send it to the server through the socket.

I ran into the same problem with the ClientHandler class but that was easy to get around since that class is initialized in the Server class, where the accept call is. Maybe I'm confused on how sockets work, but to transfer data between client and server components I have to use the fd created with the accepted call right? If so how am I supposed to get this fd to be used in the Sender class?

If you guys could point me to how I can do this on my program it would be great. I'm pretty much running out of time with this, so I can't afford to make big changes on the code right now.

Also I keep getting this error on the Client class:
    Client.cpp:8: error: invalid conversion from 'const char*' to 'char'.
The Client class is not done yet so the code is not complete.

I'll post the codes to the classes related to the problem.
I hope I wasn't that confusing with the explanation of my problem. Also if you guys need me to explain how the classes work or need to see the code of the other classes or the headers feel free to ask. If you find anything I'm doing wrong in these codes please tell me.

Thanks in advance.

Server.cpp
#include "Server.h"
#include "Socket.h"
#include "ServerDispatcher.h"
#include "ClientHandler.h"

#include <exception>
#include <iostream>

const int port = 9000;

Server::Server ( )
{
}

Server::~Server ( )
{
}

int main ( )
{
   ServerDispatcher serverDispatcher;

   Socket serverSocket;
   serverSocket.Listen( port );

   while ( true )
   {
      Socket dataSocket;
      serverSocket.Accept( dataSocket );
      ClientHandler clientHandler = ClientHandler( dataSocket );
      serverDispatcher.AddClient ( &clientHandler );
   }
}

Open in new window


Client.cpp (not yet complete)
#include "Client.h"
#include "Socket.h"
#include "Sender.h"
#include <string>
#include <iostream>

const int port = 9000;
char hostname = "127.0.0.1";

Client::Client ( )
{
}

Client::~Client ( )
{
}

void Client::GetUsername ( ) {

}

int main ( )
{
   Sender sender;
   Socket clientSocket;
   clientSocket.Connect ( hostname, port );
}

Open in new window


Sender.cpp (is a thread that will be initialized in the Client class, also not yet complete)
#include "Sender.h"
//#include "Message.h"
#include "Socket.h"
#include <string>
#include <vector>
#include <iostream>

Sender::Sender ( )
{
   pthread_create(&m_thread, 0, &Sender::StartThread, this);
}

Sender::~Sender ( )
{
}

void Sender::run ( )
{
   while( true )
   {
      std::getline( std::cin, m_message );
      if ( m_message == "/quit" )
      {
         break;
      }

      //The message need to be sent to the fd here.

   }
}

void * Sender::StartThread ( void *arg )
{
   reinterpret_cast<Sender *>(arg)->run( );
}

Open in new window


Socket.cpp
#include "Socket.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cerrno>
#include <string>
#include <iostream>

Socket::Socket( )
{
}

Socket::~Socket( )
{
}

void Socket::Listen ( const int port )
{
   int yes = 1;
   struct sockaddr_in address;

   //Create socket and check if it worked
   if ( (m_socketFD = socket(PF_INET, SOCK_STREAM, 0)) < 0 )
   {
      std::cerr << "Error creating socket :: " << strerror( errno ) << std::endl;
      return;
   }

   //Set socket to allow multiple connections
   if ( setsockopt(m_socketFD, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0 )
   {
      std::cerr << "Error setting socket option :: " << strerror( errno ) << std::endl;
      return;
   }

   address.sin_family = PF_INET;
   address.sin_addr.s_addr = INADDR_ANY;
   address.sin_port = htons( port );

   //Bind socket to the specified port
   if ( bind( m_socketFD, (struct sockaddr *)&address, sizeof( address )) < 0 )
   {
      std::cerr << "Error binding socket to port :: " << strerror( errno ) << std::endl;
      return;
   }

   //Listen for connection calls
   if ( listen( m_socketFD, 20 ) < 0 )
   {
      std::cerr << "Error listening for connections :: " << strerror( errno ) << std::endl;
      return;
   }
}

void Socket::Connect ( char hostname, int port )
{
   struct sockaddr_in address;

   //Create socket and check if it worked
   if ( (m_socketFD = socket(PF_INET, SOCK_STREAM, 0)) < 0 )
   {
      std::cerr << "Error creating socket :: " << strerror( errno ) << std::endl;
      return;
   }

   char * host = &hostname;

   address.sin_family = PF_INET;
   address.sin_port = htons( port );
   address.sin_addr.s_addr = inet_addr( host );

   if ( connect( m_socketFD, (struct sockaddr *)&address, sizeof( address )) < 0 )
   {
      std::cerr << "Error connecting to server :: " << strerror( errno ) << std::endl;
      return;
   }
}

void Socket::Accept ( Socket& socket )
{
   struct sockaddr_in address;
   int FD;

   socklen_t addrlen = sizeof( address );

   //Accept incoming connections
   if ( FD = accept( m_socketFD, (struct sockaddr *)&address, &addrlen ) < 0 )
   {
      std::cerr << "Error accepting connection :: " << strerror( errno ) << std::endl;
      return;
   }

   m_socketFD = FD;
}

void Socket::Close ( )
{
   //Close socket
   if ( close( m_socketFD ) < 0 )
   {
      std::cerr << "Error closing socket :: " << strerror( errno ) << std::endl;
      return;
   }
}

int Socket::SendData ( std::string packedMessage )
{
   std::string *message = &packedMessage;

   //Send packed message to socket
   if ( send( m_socketFD, message, sizeof( message ), 0) < 0 )
   {
      std::cerr << "Error sending to socket :: " << strerror( errno ) << std::endl;
      return -1;
   }

   return 1;
}

int Socket::RecvData ( char buffer )
{
   char * buf = &buffer;
   //Get packed message from socket
   if ( recv( m_socketFD, buf, sizeof( buf ) - 1, 0 ) < 0 )
   {
      std::cerr << "Error receiving from socket :: " << strerror( errno ) << std::endl;
      return -1;
   }

   return 1;
}

Open in new window




0
Comment
Question by:PDamasceno
  • 4
  • 3
7 Comments
 
LVL 5

Assisted Solution

by:xdomains
xdomains earned 500 total points
Comment Utility
in client.cpp instead of char hostname = "127.0.0.1";
use char hostname[] = "127.x.x.x"; to solve the error
0
 
LVL 5

Accepted Solution

by:
xdomains earned 500 total points
Comment Utility
Coming to the client's connection to the server socket, please note that the socket objects on both client and server are completely independent objects.

The server creates a socket, binds to the port and listens. Then when a connection comes accepts the connection.

The client creates a socket (no bind required), connects to the remote (or local) server using the IP address and port of the server. The socket descriptor created on client side is to be passed in the connect call. ie, the connect call will connect the local socket with the remote server IP/port. Socket is a logical abstraction for the IP/port combination on the server side, and the clients will not access the server's socket descriptor. Rather, clients just use the IP/port number of the server.

Hope this helps. Let me know if you have any question on any specific part of the code.
0
 

Author Comment

by:PDamasceno
Comment Utility
Thanks xdomains.
The solution to the Client.cpp problem worked fine, just had to do some minor changes on the Connect function of the Socket class.

So I just have to use the socket descriptor of the client side to send and receive data?
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 5

Assisted Solution

by:xdomains
xdomains earned 500 total points
Comment Utility
Yes, use the socket descriptor on the client side in your connect(), send(), recieve() calls.
0
 

Author Comment

by:PDamasceno
Comment Utility
Nice.
Well, thanks xdomains.
Everything is more clear for me now.

One more thing, how can I test a descriptor to see if there is data to be received?
The Client class will stay in an infinite loop "listening" the descriptor for messages to be received and if there is one, it will show to the client.
0
 
LVL 5

Assisted Solution

by:xdomains
xdomains earned 500 total points
Comment Utility
use select(). If there is data in the pipe, it will return. You need to put select() in a loop, and when it returns, do a recieve().
0
 

Author Comment

by:PDamasceno
Comment Utility
Thanks for the help!
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

The purpose of this article is to fix the unknown display problem in Linux Mint operating system. After installing the OS if you see Display monitor is not recognized then we can install "MESA" utilities to fix this problem or we can install additio…
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now