LinuxQuestions.org
View the Most Wanted LQ Wiki articles.
Go Back   LinuxQuestions.org > Blogs > rainbowsally
User Name
Password

Notices

Rate this Entry

Sockets - UDP RX (recieve/transmit) terminal example

Posted 01-17-2013 at 06:15 PM by rainbowsally
Updated 01-17-2013 at 06:16 PM by rainbowsally

Sockets - UDP RX (recieve/transmit) terminal example
Today's feature:
  • A UDP file server and client using linux sockets.
  • A terminal app for those who don't have libLQ-qt and mc2.

[This is for our LQ-less friends. A libLQ version using a qt gui but NOT qt sockets should be forthcoming shortly.]

This upload is pushing the size limit but it's interesting to see how the broadcast channel is set up (in inc/broadcast-rx.c) and how the parts of the files are picked up and reassembled at the receiving end.

If you have 'launch' (available here at the blog) if you run 'launch xterm' and then in xterm run 'file-server o/file-server.o' it will broadcast the object file, then in your main terminal type 'file-client' and watch it assemble from the offsets. (If you compile with -g3, the file will be large enough to watch this effect. It will create the d/loaded file in the directory where you run file-client.)

file: file-server.c
purpose: source file
Code:
/* file-server.c
 * Based on files at 
 *    www.cs.utah.edu/~swalton/listings/sockets/programs/index.html
 *
 * Copyright (c) 2001 Sean Walton and Macmillan Publishers.  Use may be in
 * whole or in part in accordance to the General Public License (GPL).
 *
 * Modified by Rainbow Sally Copyright (c) 2013, GPL 3+
 */

// Demonstrates a UDP file server.

#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <resolv.h>
#include "broadcast-rx.h"
#include "inc/broadcast-rx.c"

#include <stdlib.h>     // abort(), exit(), atoi()
#include <string.h>     // strcmp(), memset()
#include <arpa/inet.h>  // inet_addr, etc.
#include <unistd.h>     // close()

// send packet.
void transmit(int sd, struct bfile *bfile)
{
  bfile->header.hash = 0;
  bfile->header.checksum = 0;
  bfile->header.hash = htonl(dx_hack_hash(bfile->buffer, ntohs(bfile->header.size)));
  bfile->header.checksum = htons(checksum(bfile, sizeof(bfile->header)+ntohs(bfile->header.size)));
  send(sd, bfile, sizeof(bfile->header)+ntohs(bfile->header.size), 0);
// ---->
//  printf("Sent offset=%d size=%d (%d total bytes)\n", ntohl(bfile->header.offset),
//    ntohs(bfile->header.size), sizeof(bfile->header)+ntohs(bfile->header.size));
// <----
}


// take apart a file and send it piece by piece.
void sender(int sd, char* filename, u16 bufsize)
{ int fd;
  int offset, bytes;
  struct stat stat;
  struct bfile bfile;
  struct bfile_info finfo;

  if ( (fd = open(filename, O_RDONLY)) < 0 )
    panic(filename);
  if ( fstat(fd, &stat) < 0 )
    perror("fstat");
  memset(&finfo, 0, sizeof(finfo));
  if ( strrchr(filename, '/') == NULL )
    strncpy(finfo.filename, filename, MAXNAME);
  else
    strncpy(finfo.filename, strrchr(filename, '/')+1, MAXNAME);
  finfo.filename[MAXNAME-1] = 0;
  finfo.id = htons(getpid());
  finfo.modified = stat.st_mtime;
  finfo.fsize = htonl(stat.st_size);
  bfile.header.id = htons(getpid());
  while (1)
  {
    offset = -1;
    bytes = sizeof(finfo);
    memcpy(bfile.buffer, &finfo, bytes);
    do
    {
      bfile.header.offset = htonl(offset);
      bfile.header.size = htons(bytes);
      transmit(sd, &bfile);
      sleep(1);
      if ( offset == -1 )
        offset = 0;
      else
        offset += bytes;
      bytes = read(fd, bfile.buffer, bufsize);
    }
    while ( offset < stat.st_size );
    lseek(fd, 0, SEEK_SET);
  }
  close(fd);
}

// set up and start the UDP file server.
int main(int argc, char *argv[])
{ 
  if ( argc != 2 )
  {
    printf(" Usage: %s <filename>\n", argv[0]);
    exit(0);
  }

  char* addr_str = "127.0.0.1";
  char* port_str = "9999";
  char* filename_str = argv[1];
  int bufsize = 32768;

  int sd; //, fd;
  struct in_addr in_addr;
  struct ip_mreq mreq;
  struct sockaddr_in addr;


  printf(
    "Uploading local file '%s'\n"
     "Using addr '%s', port %s\n"
     "\n"
     "This program sends data over and over non-stop\n"
     "in 32K chunks using the UDP protocol.\n"
     "\n"
     "The 'file-client' picks up at the current file offset\n"
     "and continues to download until all the blocks\n"
     "have been downloaded\n\n"
     , filename_str, addr_str, port_str
  );

  sd = socket(PF_INET, SOCK_DGRAM, 0);
  if ( inet_aton(addr_str, &in_addr) == 0 )
    panic(addr_str);
  if ( ismulticast(in_addr) )
  {
    mreq.imr_multiaddr = in_addr;
    mreq.imr_interface.s_addr = INADDR_ANY;
    if ( setsockopt(sd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != 0 )
      panic("Can't add membership");
  }
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(atoi(port_str));
  addr.sin_addr = in_addr;
  if ( connect(sd, (void*)&addr, sizeof(addr)) != 0 )
    panic("Can't connect to address");
  sender(sd, filename_str, bufsize);
  close(sd);
  return 0;
}

file: file-client.c
purpose: source file
Code:
/* file-client.c
 *
 * Based on files found at
 *  www.cs.utah.edu/~swalton/listings/sockets/programs/index.html
 * 
 * Copyright (c) 2001 Sean Walton and Macmillan Publishers.  Use may be in
 * whole or in part in accordance to the General Public License (GPL).
 *
 * Modified by Rainbow Sally Copyright (c) 2013 Rainbow Sally GPL 3+
*/

// receiving multicast files over UDP

#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <resolv.h>

#include "broadcast-rx.h"
#include "inc/broadcast-rx.c"

#include <stdlib.h>     // abort(), exit(), atoi()
#include <string.h>     // strcmp(), memset()
#include <arpa/inet.h>  // inet_addr, etc.
#include <unistd.h>     // close()

#define max(a,b)  ((a)>(b)?(a):(b))
#define min(a,b)  ((a)>(b)?(b):(a))

#define MAXTABLE    5
#define MAXFRAGS    20

#define WENT_OKAY   0
#define BAD_CHECKSUM  -1
#define BAD_HASH    -2
#define BAD_SEEK    -3

//static int cnt=0;
static struct filerec
{
  u32 ipaddr;
  u16 pid;
  int ffd;
  u32 fsize;
  char filename[MAXNAME];
  char ftemp[MAXNAME];
  struct fragstruct
  {
    u32 head;
    u32 tail;
  } frags[MAXFRAGS];
} ftable[MAXTABLE];


// addfrag - track the received data range (overlapping allowed).
int addfrag(struct filerec* filerec, u32 offset, u32 size)
{ 
  int i, added=0;
  struct fragstruct* frags = filerec->frags;
  struct fragstruct new={offset, offset+size};

  for ( i = 0; i < MAXFRAGS  &&  frags[i].tail != 0; i++ )
    if ( (frags[i].head <= new.head  &&  new.head <= frags[i].tail)  ||
       (new.head <= frags[i].head  &&  frags[i].head <= new.tail) )
    { int j;
      u32 tail=max(new.tail, frags[i].tail);

      for ( j = i+1; j < MAXFRAGS  &&  frags[j].head <= new.tail  &&  frags[j].tail != 0; j++ )
        tail = max(frags[j].tail, tail);
      new.head = min(new.head, frags[i].head);
      new.tail = max(new.tail, tail);
      frags[i] = new;
      while ( j < MAXFRAGS  &&  frags[j].tail != 0 )
        frags[++i] = frags[j++];
      while ( ++i < MAXFRAGS  &&  frags[i].tail != 0 )
        frags[i].tail = 0;
      added = 1;
      break;
    }
    else if ( new.tail < frags[i].head )
    { struct fragstruct temp;
      int j=i;
      do
      {
        temp = frags[j];
        frags[j] = new;
        new = temp;
      }
      while ( j < MAXFRAGS-1  &&  frags[j++].tail != 0 );
      added = 1;
      break;
    }
  if ( !added )
    frags[i] = new;
#ifdef DEBUG
  printf("Adding: Head=%d Tail=%d (offset=%d, size=%d)\n", new.head, new.tail, offset, size);
  for ( i = 0; i < MAXFRAGS  &&  frags[i].tail != 0; i++ )
    printf("#%d: head=%03d tail=%03d [%d]\n", i+1, frags[i].head, frags[i].tail, filerec->fsize);
  printf("------\n");
#endif
  return (filerec->fsize == (frags[0].tail - frags[0].head));
}

// find or allocate destination files.

#define TEMPFILE  "image.XXXXXX"
#define INDXFILE  "index.XXXXXX"

int getfd(u32 addr_in, u16 pid)
{ int i;

  for ( i = 0; i < MAXTABLE; i++ )
    if ( ftable[i].ipaddr == addr_in  &&  ftable[i].pid == pid )
      return i;
  for ( i = 0; i < MAXTABLE; i++ )
    if ( ftable[i].ipaddr == 0 )
      break;
  if ( i < MAXTABLE )
  {
    ftable[i].ipaddr = addr_in;
    ftable[i].pid = pid;
    strcpy(ftable[i].ftemp, TEMPFILE);
    ftable[i].ffd = mkstemp(ftable[i].ftemp);
    if ( ftable[i].ffd >= 0 )
      return i;
    else
      return -1;
  }
  return -1;
}

// put data fragment in file and register range.
int storerec(struct sockaddr_in *addr, struct bfile *bfile, int* finished)
{ int index;
  u32 offset = ntohl(bfile->header.offset);
  u16 size = ntohs(bfile->header.size);

  index = getfd(ntohl(addr->sin_addr.s_addr), ntohs(bfile->header.id));
  if ( index < 0 )
    fprintf(stderr, "Can't find or add new file stream\n");
  else if ( lseek(ftable[index].ffd, offset, SEEK_SET) >= 0 )
  {
    write(ftable[index].ffd, bfile->buffer, size);
    if ( addfrag(&ftable[index], offset, size) )
    {
      close(ftable[index].ffd);
      rename(ftable[index].ftemp, ftable[index].filename);
      printf("File %s complete!\n", ftable[index].filename);
      memset(&ftable[index], 0, sizeof(ftable[index]));
      *finished = 1; // true
    }
  }
  else
    return BAD_SEEK;
  return WENT_OKAY;
}

// check packet; if header, set data, otherwise, put data to file                                                  ---*/
int validwrite(struct sockaddr_in *addr, struct bfile* bfile, int* finished)
{ u16 sum;
  u32 hash;

  sum = ntohs(bfile->header.checksum);
  bfile->header.checksum = 0;
  if ( checksum(bfile, sizeof(bfile->header)+ntohs(bfile->header.size)) != sum )
    return BAD_CHECKSUM;
  hash = ntohl(bfile->header.hash);
//  bfile->header.hash == 0;
  bfile->header.hash = 0;
  if ( dx_hack_hash(bfile->buffer, ntohs(bfile->header.size)) != hash )
    return BAD_HASH;
  if ( bfile->header.offset == -1 )
  { struct bfile_info *finfo = (void*)bfile->buffer;
    int index;
printf("file=%s size=%d date=%s", finfo->filename, ntohl(finfo->fsize), ctime(&finfo->modified));
    index = getfd(ntohl(addr->sin_addr.s_addr), ntohs(bfile->header.id));
    ftable[index].fsize = ntohl(finfo->fsize);
    strncpy(ftable[index].filename, finfo->filename, MAXNAME);
    ftable[index].filename[MAXNAME-1] = 0;
  }
  else
    return storerec(addr, bfile, finished);
  return WENT_OKAY;
}

// receive buffers and show offsets and order received in.
void receiver(int sd)
{ 
  struct bfile bfile;
  struct sockaddr_in addr;
  int finished;
  do
  { 
    finished = 0;
    size_t len=sizeof(addr);
    int bytes;
    bytes = recvfrom(sd, (void*)&bfile, sizeof(bfile), 0, (void*)&addr, &len);
// --->
    printf("Received offset=%d size=%d from %s:%d\n", ntohl(bfile.header.offset),
    ntohs(bfile.header.size), inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
// <---
    switch ( validwrite(&addr, &bfile, &finished) )
    {
      case BAD_SEEK: perror("Could not seek"); break;
      case BAD_CHECKSUM: fprintf(stderr, "Failed checksum\n"); break;
      case BAD_HASH: fprintf(stderr, "Failed hash check\n"); break;
    }
    if(finished)
      break;
  }
  while ( 1 );
}

// set up socket and run.
int main(int argc, char *argv[])
{ 
  char* addr_str = "127.0.0.1";
  char* port_str = "9999";
  printf("Attempting download of local file from local file server\n");
  printf("Using addr %s, port %s\n", addr_str, port_str);
  int sd; //, fd;
  const int on=1;
  struct in_addr in_addr;
  struct ip_mreq mreq;
  struct sockaddr_in addr;

  sd = socket(PF_INET, SOCK_DGRAM, 0);
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(atoi(port_str));
  addr.sin_addr.s_addr = INADDR_ANY;
  if ( bind(sd, (void*)&addr, sizeof(addr)) != 0 )
    panic("Can't bind to port");
  if ( setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0 )
    panic("Can't enable port sharing");
  if ( inet_aton(addr_str, &in_addr) == 0 )
    panic(addr_str);
  if ( ismulticast(in_addr) )
  {
    mreq.imr_multiaddr = in_addr;
    mreq.imr_interface.s_addr = INADDR_ANY;
    if ( setsockopt(sd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != 0 )
      panic("Can't add membership");
  }
  receiver(sd);
  close(sd);
  return 0;
}

NOTE THE PATH BELOW

file: inc/broadcast-rx.c
purpose: source file
Code:
/* broadcast-rx.c
 *
 * Based on files found at
 *  www.cs.utah.edu/~swalton/listings/sockets/programs/index.html
 * 
 * Copyright (c) 2001 Sean Walton and Macmillan Publishers.  Use may be in
 * whole or in part in accordance to the General Public License (GPL).
 *
 * Modified by Rainbow Sally Copyright (c) 2013 Rainbow Sally GPL 3+
*/

#include <stdio.h>
#include "broadcast-rx.h"
#include <arpa/inet.h>  // inet_addr, etc.

// relatively uniform hash, not using the libLQ version, this is the original.
// NOTE: This is used by the transmitter and receiver, so this is not going to 
// for apps that don't use this exact algorithm.  A simple checksum should be 
// adequate, especially for small block sizes. -rs
u32 dx_hack_hash(const void *d, int len)
{ 
  const char *data = d;
  u32 hash0 = 0x12A3FE2D;
  u32 hash1 = 0x37ABE8F9;
  while ( len-- )
  {
    u32 hash = hash1 + (hash0 ^ (*data++ * 71523));
    if ( hash < 0 )
      hash -= 0x7FFFFFFF; /* when is hash going to be less than zero if unsigned? */
    hash1 = hash0;
    hash0 = hash;
  }
  return hash0;
}

// calculate the checksum of data.  Again, this is overkill and requires the receiver 
// and transmitter to use the exact same algorithm. -rs
u16 checksum(const void *d, int len)
{
  u32 sum = 0;
  const unsigned char *data = d;

  while ( len-- )
    sum += *data++;
  sum = (sum & 0xFFFF) + (sum >> 16);
  sum = (sum & 0xFFFF) + (sum >> 16);
  return sum;
}

// ismulticast - check range for multicast address.
int ismulticast(struct in_addr in_addr)
{ struct in_addr MCAST_LOWER, MCAST_UPPER;

  inet_aton("224.0.0.0", &MCAST_LOWER);
  inet_aton("239.255.255.255", &MCAST_UPPER);
  return (ntohl(MCAST_LOWER.s_addr) <= ntohl(in_addr.s_addr)  &&
      (ntohl(in_addr.s_addr) <= ntohl(MCAST_UPPER.s_addr)) );
}
If you don't have mc2, this should at least suggest how to set up your own makefile. Makefiles lose their TABs when I upload and there's a lot of 'em, so... Yer on your own here.

This is a type MULTI mc2.def that creates the output files in the same directory with the Makefile.

file: mc2.def
purpose: Makefile generation with mc2
Code:
# mc2.def template created with Makefile Creator 'mc2'

# sandbox path and other new variables
PREFIX = $(HOME)/usr32
BUILDDIR := $(PWD)

OUTNAME = MULTI

SRCDIR = .
OBJDIR = o
BINDIR = .

# compile function overrides
COMPILE = gcc -m32 -c -o # COMPILE <output_file> (for C, 32 bit, object)
 CFLAGS = -Wall -g3 # debug
# CFLAGS = -Wall -O2 # optimized
INCLUDE = -I $(SRCDIR) -I$(PREFIX)/include -I /usr/include 

# link function overrides
LINK = gcc -m32 -o
LDFLAGS = 
LIB = -L$(PREFIX)/lib -L/usr/lib 
 
semiclean:
  @rm -f $(OBJ)
  @rm -f *~ */*~ */*/*~ */*/*/*~

strip:
  @strip $(MAIN)
  @make semiclean

# additional targets
#mc2-update:
#  @mc2 -update

clean:
  @rm -f $(MAIN)
  @rm -f $(OBJ)
  @rm -f *~ */*~ */*/*~ */*/*/*~

force: # used to force execution
The Computer Mad Science Team

;-)
Posted in Uncategorized
Views 595 Comments 0
« Prev     Main     Next »
Total Comments 0

Comments

 

  



All times are GMT -5. The time now is 05:00 PM.

Main Menu

My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration