Architecture/Lecture/tcpsrv0.1

From Apache OpenOffice Wiki
< Architecture‎ | Lecture
Revision as of 21:02, 15 May 2008 by Sz (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
/* -*- 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>
 *
 * tcqpsrv.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 <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 <sys/select.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)


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 = sizeof(sockadr);

  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;                    /* max num of socket file descriptor in set */

  /* 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;             /* file descriptor set for reading */
    int ret;                    /* return value for error handling in macros */
    int client_rfd = 0;         /* client file descriptor to read from */

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

    /* wait for activity */
    ret = select(maxfd + 1, &readfds, NULL, 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