Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

Question about socket programming.

Posted on 2010-09-09
7
Medium Priority
?
516 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 3
7 Comments
 
LVL 5

Assisted Solution

by:xdomains
xdomains earned 2000 total points
ID: 33635804
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 2000 total points
ID: 33635881
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
ID: 33636001
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
Free learning courses: Active Directory Deep Dive

Get a firm grasp on your IT environment when you learn Active Directory best practices with Veeam! Watch all, or choose any amount, of this three-part webinar series to improve your skills. From the basics to virtualization and backup, we got you covered.

 
LVL 5

Assisted Solution

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

Author Comment

by:PDamasceno
ID: 33636049
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 2000 total points
ID: 33636100
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
ID: 33636141
Thanks for the help!
0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

What is C++ STL?: STL stands for Standard Template Library and is a part of standard C++ libraries. It contains many useful data structures (containers) and algorithms, which can spare you a lot of the time. Today we will look at the STL Vector. …
This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.
Suggested Courses

610 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