Architecture/Lecture/tcpsrv0.2

From Apache OpenOffice Wiki
Jump to: navigation, search

tcpsrv.c a simple TCP socket server (version 0.2)

The server can be connected by multiple telnet sessions sending strings to the server which sends the received strings to all other connected telnet sessions (simple chat functionality). Disconnect client by sending the string "quit". The code is heavily commented for learning purposes, which maybe wouldn't be done to this extension in real program code.

Build, start and usage is described in the comment header

This is merely the same than version 0.1 due to the approach there to do it right away with select(), it is just split in .h and .c files for more modularity and better maintainability

here is tcpsrv.h

/* include tcpsrv.h */
/* Tabs are set for 4 spaces, not 8 */

#ifndef	__tcpsrv_h
#define	__tcpsrv_h


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <syslog.h>
#include <fcntl.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>


/* the makros exit_if() and return_if() realize the error handling
 * of the application. If exit_if() condition is TRUE, the program will
 * be terminated with the information of: file, line, function and errno.
 *
 * If the return_if() condition is TRUE, the program returns from the
 * current function. The second parameter will be used as return value
 */

#define exit_if(expr)\
if(expr) {\
  syslog(LOG_WARNING, "exit_if() %s: %d: %s: Error %s\n",\
  __FILE__, __LINE__, __PRETTY_FUNCTION__, strerror(errno));\
  exit(1);\
}

#define return_if(expr, retvalue)\
if(expr) {\
  syslog(LOG_WARNING, "return_if() %s: %d: %s: Error %s\n\n",\
  __FILE__, __LINE__, __PRETTY_FUNCTION__, strerror(errno));\
  return(retvalue);\
}

#define MAXLEN 1024
#define PORT 61412
#define OKAY 0
#define ERROR (-1)

struct rw_buf{
  char data[MAXLEN];
  int  datalen;
  char *inptr;
  char *outptr;
};

struct connection {
  int hndl;
/*   struct addr_data addrdata; */
  char *inptr;
  char *outptr;
};

#endif

and tcpsrv.c

/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * tcpsrv.c Vers. 0.1
 * Copyright (C) Stefan Zimmermann 2008 <sz@sun.com>
 *
 * tcpsrv.c is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * tcpsrv.c is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 *    
 * The server waits on TCP Port PORT for connection requests from
 * telnet clients. The client sends ASCII messages to the server.
 * This messages will be sent from the server then to all other connected clients.
 *    
 * start server with:
 *  ./tcpsrv &
 *    
 * start client with:
 *  telnet _serverIPadresse_ PORT
 */   
      
#include "tcpsrv.h"
#include <sys/select.h>
                                          
int listen_socket(int port)               
/* open server (listen) port - called only once
 * in port: port number where TCP server is supposed to listen
 * return: server socket file descriptor to connect from client(s)
 */
{

  int srv_sockfd;
  int ret;
  struct sockaddr_in sockadr;
  int yes = 1;

  /* create the socket */
  srv_sockfd = socket(PF_INET, SOCK_STREAM, 0);
  exit_if(srv_sockfd < 0);

  /* prevent "error address already in use" message */
  ret = setsockopt(srv_sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
  exit_if(ret < 0);

  memset((char *) &sockadr, 0, sizeof(sockadr));
  sockadr.sin_family = AF_INET;
  sockadr.sin_addr.s_addr = htonl(INADDR_ANY);
  sockadr.sin_port = htons(port);

  /* bind created socket to port */
  ret = bind(srv_sockfd, (struct sockaddr *) &sockadr, sizeof(sockadr));
  exit_if(ret != 0);

  ret = listen(srv_sockfd, 5);
  exit_if(ret < 0);

  return srv_sockfd;
}

int accept_socket(int srv_sockfd)
/* open communication (connection) - execute for every new client
 * in srv_sockfd: socket file descriptor to connect from client
 * return: if okay socket file descriptor to read from client, ERROR otherwise
 */
{
  int client_fd;
  struct sockaddr_in sockadr;
  socklen_t socklen;

  socklen = sizeof(sockadr);
  client_fd = accept(srv_sockfd, (struct sockaddr *) &sockadr, &socklen);
  return_if(client_fd < 0, ERROR);

  printf("Connected %s:%d\n",
		 (char*)inet_ntoa(sockadr.sin_addr), ntohs(sockadr.sin_port));


  return client_fd;
}

int socket_write(int client_fd, char buf[], int buflen)
/* write to the client socket interface
 * in fd: socket file descriptor to write to client
 * in buf: message to write
 * in buflen: length of message
 * return: OKAY if write was completed, ERROR otherwise
 */
{
  int ret;

  ret = send(client_fd, buf, buflen, 0);
  return_if(ret != buflen, ERROR);
  return OKAY;
}

int socket_read(int client_fd, char buf[], int *buflen)
/* read from client socket interface
 * in fd: socket file descriptor to read from client
 * out buf: message which was read
 * inout buflen: in = max length of message, out =  length of message read
 * return: OKAY when read was completed, ERROR otherwise
 */
{
  /* read message */
 	buflen = recv(client_fd, buf, *buflen, 0);
    
  if (buflen <= 0 ||  strncmp("quit\r", buf,5) == 0) {
    /* end of TCP connection */
    close(client_fd);
    return ERROR;               /* means fd is not valid anymore */
  }
  return OKAY;
}
							
void srv_loop(int srv_sockfd)
/* server loop              
 * in listen_fd: socket file descriptor to connect from client
 */                         
{                           
  fd_set sock_fdset;            /* socket file descriptor set */
  int maxfd, val;               /* max num of socket fd in set, fdflags */
  struct rw_buf rbuf, wbuf; 
                            
  val = fcntl(srv_sockfd, F_GETFL, 0);
  fcntl(srv_sockfd, F_SETFL, val | O_NONBLOCK);
                            
  rbuf.inptr = rbuf.outptr = rbuf.data;
  wbuf.inptr = wbuf.outptr = wbuf.data;
  /* zero the file descriptor set to get rid of random values
     then copy server listen socket fd to file descriptor set */
  FD_ZERO(&sock_fdset);     
  FD_SET(srv_sockfd, &sock_fdset);
  maxfd = srv_sockfd;         
                              
  for (;;) {                    /* begin loop */
	fd_set readfds, writefds;   /* file descriptor sets for read and write */
    int ret;                    /* return value for error handling in macros */
    int client_rfd, client_wfd = 0;   /* client fd to read from/write to */

    readfds = sock_fdset;       /* select() changes readfds, backup state */
    writefds = sock_fdset;

    /* wait for activity */
    ret = select(maxfd + 1, &readfds, &writefds, NULL, NULL);
    if ((ret == -1) && (errno == EINTR)) {
      /* a signal occurred - ignore */
      continue;
    }
    exit_if(ret < 0);

    /* check TCP server LISTEN Port for client connect request
       if, then accept the connect request and add new client to
       our socket file descriptor set */
    if (FD_ISSET(srv_sockfd, &readfds)) {
	  client_rfd = accept_socket(srv_sockfd);
      if (client_rfd >= 0) {
        FD_SET(client_rfd, &sock_fdset);        /* add new client rfd  */
        if (client_rfd > maxfd) {               /* re-adjust max fd num */
          maxfd = client_rfd;
        }
      }
    }

    /* check TCP server CONNECT ports for clients communication */
    for (client_rfd = srv_sockfd + 1; client_rfd <= maxfd; ++client_rfd) {
      if (FD_ISSET(client_rfd, &readfds)) {
        char msgbuf[MAXLEN];
        int msgbuflen;

        /* read message from client */
        msgbuflen = sizeof(msgbuf);
        ret = socket_read(client_rfd, msgbuf, &msgbuflen);

        if (ERROR == ret) {
          FD_CLR(client_rfd, &sock_fdset);      /* remove client rfd if error*/
        } else {
          /* write message to all other clients  */
          int client_wfd;

          for (client_wfd = srv_sockfd+1; client_wfd <= maxfd; ++client_wfd) {
		  	if (FD_ISSET(client_wfd, &sock_fdset)&&(client_rfd != client_wfd)) {
		   	   	socket_write(client_wfd, msgbuf, msgbuflen);
		   	} /* if */
          } /* for */
        } /* else */
      } /* if */
    } /* for */
  } /* end of for loop */
} /* end of srv_loop function */

int main(int argc, char *argv[])
{
  /* enable error logging */
  openlog(NULL, LOG_PERROR, LOG_WARNING);

  /* open tcpsrv */
  srv_loop(listen_socket(PORT));

  return OKAY;
}

Personal tools