Thursday, April 19, 2012

The linux sockets

I spend a few evenings reading about linux sockets. I created client and server's applications which use sockets with TCP/IP protocol. Client sends a request to the server and gets a response. I also found an interesting article about linux sockets and networking programming. The article can be found here.

Here is a simple diagram which shows how sockets work:


Here is a header file Channel.h of the channel class which encapsulates socket:
#ifndef LAB_CHANNEL
#define LAB_CHANNEL

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

namespace lab {
 class Channel {
   int socket_id;
   sockaddr_in socket_address;
   void CreateSocket(const char* ip, int port);
   void Write(int connection_id, const char* message);
   void Read(int connection_id);
  public:
   Channel(const char* ip, int port);
   ~Channel();
   void Listen();
   void Send(const char *message);
   void Close();
 };
}

#endif
Here is a source file Channel.cpp of the Channel class:
#include "Channel.h"
#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

lab::Channel::Channel(const char* ip, int port) {
 this->CreateSocket(ip, port); 
}

lab::Channel::~Channel() {
 this->Close();
}

void lab::Channel::CreateSocket(const char* ip, int port) {
 // socket() creates an endpoint for communication and returns a descriptor.
 // AF_INET - IPv4 Internet protocols.
 // SOCK_STREAM - Provides sequenced, reliable, two-way, connection-based byte 
 // streams.
 socket_id = socket(AF_INET, SOCK_STREAM, 0);
 if (socket_id == -1) {
  std::cout << "Failed to create socket." << std::endl;
 } else {
  std::cout << "Socket was created successfully." << std::endl;
 }

 socket_address.sin_family = AF_INET;
 socket_address.sin_port = htons(port);
 socket_address.sin_addr.s_addr = inet_addr(ip);
 memset(&(socket_address.sin_zero), 0, 8);
 // bind - bind a name to a socket.
 if (bind(socket_id, (struct sockaddr *)&socket_address, sizeof(struct sockaddr)) != -1) {
  std::cout << "Name was binded successfully to the socket." << std::endl;
 } else {
  std::cout << "Failed to bind a name to the socket." << std::endl;
 } 
}

void lab::Channel::Write(int connection_id, const char* message) {
 if (write(connection_id, message, strlen(message)) != -1) {
  std::cout << "Message was send: " << message << std::endl;
 } else {
  std::cout << "Failed to send a message." << std::endl;
 }
}

void lab::Channel::Read(int connection_id) {
 char buffer[32];
 memset(buffer, 0, 32);
 if (read(connection_id, buffer, 32) != -1) {
  std::cout << "Message was read: " << buffer << std::endl;
 } else {
  std::cout << "Failed to read a message." << std::endl;
 }
}

void lab::Channel::Listen() {
 // listen - listen for connections on a socket.
 // The backlog argument defines the maximum length to which the queue of 
 // pending connections for sockfd may grow.
 if (listen(socket_id, 4) != -1) {
  std::cout << "Listening the socket." << std::endl;
 } else {
  std::cout << "Failed to listen the socket." << std::endl;
 }

 // accept - accept a connection on a socket.
 sockaddr_in remote_address;
 int connection_id;
 int remote_address_size = sizeof(remote_address);
 connection_id = accept(socket_id, (sockaddr*)&remote_address, (socklen_t*)&remote_address_size);
 if (connection_id != -1) {
  std::cout << "Connection was accepted." << std::endl;
 } else {
  std::cout << "Failed to accept connection." << std::endl;
 }

 this->Read(connection_id);
 this->Write(connection_id, "Reply");

 close(connection_id);
}

void lab::Channel::Send(const char *message) {
 // connect - initiate a connection on a socket.
 if (connect(socket_id, (sockaddr*)&socket_address, sizeof(sockaddr)) != -1) {
  std::cout << "Connection was estableshed successfully." << std::endl;
 } else {
  std::cout << "Failed to connect." << std::endl;
 }
 this->Write(socket_id, message);
 this->Read(socket_id);
}

void lab::Channel::Close() {
 close(socket_id);
}
Here is a file Client.cpp of the client application:
#include <iostream>
#include "Channel.h"

int main() {
 std::cout << "Client started..." << std::endl;

 lab::Channel *channel = new lab::Channel("127.0.0.1", 2590);
 channel->Send("Request");
 delete channel;

 return 0;
}
Here is a file Server.cpp of the client application:
#include <iostream>
#include "Channel.h"

int main() {
 std::cout << "Service started..." << std::endl;

 lab::Channel *channel = new lab::Channel("127.0.0.1", 2590);
 channel->Listen();
 delete channel;

 return 0;
}
Also I added makefile which allows to compile source code:
CPP = g++
CFLAGS = -Wall -lrt
CHANNEL_FILES = src/Channel.cpp
SERVICE_FILES = src/Service.cpp
CLIENT_FILES = src/Client.cpp

all: service client

service: channel $(SERVICE_FILES)
 $(CPP) $(CFLAGS) -o bin/service obj/channel.o $(SERVICE_FILES)

client: channel $(CLIENT_FILES)
 $(CPP) $(CFLAGS) -o bin/client obj/channel.o $(CLIENT_FILES)

channel: $(CHANNEL_FILES)
 $(CPP) $(CFlAGS) -o obj/channel.o -c $(CHANNEL_FILES)

clean:
 rm -f ./obj/*.o
 rm -f ./bin/service
 rm -f ./bin/client

run-service:
 ./bin/service

run-client:
 ./bin/client

No comments:

Post a Comment