Использование C-сокета из Solaris в Ubuntu - PullRequest
0 голосов
/ 27 марта 2012

следующий с-код был запрограммирован для соляриса.Теперь мне пришлось перенести его на Linux Ubuntu:

UDP клиент

/* Modul fuer Echo-Client mittels UDP
   Autor K. Felten                Letzte Aenderung: 02.04.2008
                                                  Anpassung an Solaris 9
Aufruf mit: udpclient IP-Addr UDP-Port                                                  
------------------------------------------------------------------------------
* Example of server using UDP protocol.
------------------------------------------------------------------------------
As with the TCP server example, the Internet address for the bind is specified
as INADDR_ANY.
The client program:
------------------------------------------------------------------------------
*/
#include "inet.h"
#include <netdb.h>
#include <ctype.h>

int check_dot( address )
char *address;
{  int dotcount = 0;
   for ( ; *address != '\0'; address++ )
      if ( *address == '.' )
         dotcount++;
      else if (!isdigit(*address))
              dotcount = 4; 
   return ( dotcount );
}

int main(int argc, char *argv[])
{
   int sockfd;
   int i, *iaddr;
   unsigned char *addr;
   struct sockaddr_in cli_addr, serv_addr;
   char *server_ip_addr;
   int dotnum; /* Number of Dots in Address (argv[1] */
   unsigned short server_port; /* Serverport-Nr. */
   struct hostent *server;

   if (argc != 3 ){
      printf("2 Arguments required:\n");
      printf(" - IP-Address, Dot Notation\n");
      printf(" - UDP-Port-Nr.\n");
      exit(1);
   }
   else server_ip_addr = argv[1];
   dotnum = check_dot(server_ip_addr);
   if ( dotnum != 3 )
   {  /* Address not in Dot-Notation */
      server = gethostbyname( server_ip_addr );
      if ( server != NULL )
      {  iaddr = (int * ) *(server->h_addr_list); /* get 4 Byte Internet-addr. */
         /* Testausgaben */
         printf("Server-Name      =%s\n", server->h_name );
         printf("Server-Addr_length=%d\n", server->h_length );
         printf("Addr=%x\n",(unsigned int)server->h_addr_list ); 
         addr = (unsigned char * ) *(server->h_addr_list);
         printf("Addr=%x\n", *iaddr );  
         while ( *addr != 0)
         { printf("Addr=%d\n", *addr ); 
           addr++;
         }
      }
      else printf("Server-Address not found\n");
   }
   server_port = (short) atoi(argv[2]);
   /*
   * Fill in the structure "serv_addr" with the address of the
   * server that we want to send to.
   */
   bzero((char *) &serv_addr, sizeof(serv_addr));
   serv_addr.sin_family = AF_INET;
   if ( dotnum == 3 )
      serv_addr.sin_addr.s_addr = inet_addr( server_ip_addr );
   else 
      serv_addr.sin_addr.s_addr = *iaddr;
   serv_addr.sin_port = htons(server_port);
   /*
   * Open a UDP socket (an Internet datagram socket).
   */
   if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
      err_dump("client can't open datagram socket");
   /*
   * Bind any local address for us.
   */
   bzero((char *) &cli_addr, sizeof(cli_addr)); /* zero out */
   cli_addr.sin_family = AF_INET;
   cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
   cli_addr.sin_port = htons(0);
   if (bind(sockfd, (struct sockaddr *) &cli_addr, sizeof(cli_addr)) < 0)
      err_dump("client: can't bind local address");
   /* else printf("bind ist ok\n"); */
   dg_cli(stdin, sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
   close(sockfd);
   exit ( 0 ) ;
}

есть также make-файл:

# this makefile uses your sources
APPN=echoUDP    # put your favorite program here

# change these for different maschines and stages of development
CFLAGS = -g -lnsl   # build the symbol table for debugger

# build the utilities
utilities.o: utilities.c
    $(CC) $(CFLAGS) -c utilities.c 

# build the client application
$(APPN)client: inet.h utilities.o $(APPN)client.o 
    $(CC) $(CFLAGS) -o $(APPN)client utilities.o $(APPN)client.o  $(LFLAGS)

# build the server application
$(APPN)serv: inet.h utilities.o $(APPN)serv.c 
    $(CC) $(CFLAGS) -o $(APPN)serv utilities.o $(APPN)serv.o\
     $(APPN)serv.c $(LFLAGS)            

иЗаголовочный файл inet.h:

/* Header-File fuer TCP und UDP 
* Letzte Aenderung: 02.04.2008 K. Felten
* Definitions for TCP and UDP client/server programs.
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <syslog.h>

#define SERV_HOST_ADDR "127.0.0.1" /* host addr for server = localhost */
char *pname;
void err_sys( char *errortext);
void err_dump( char *errortext);
void dg_echo(int sockfd, struct sockaddr *pcli_addr, int maxclilen);
void dg_cli(FILE *fp, int sockfd, struct sockaddr *pserv_addr, int servlen);

и, по крайней мере, utilities.c

/* Modul mit Hilfsprogrammen fuer Socket-Beispiel
* Letzte Aenderung: 02.04.2008 K. Felten
*/
#include <stdio.h>
#include <string.h>
#include <syslog.h>

char emesgstr[255] ={0};

/* Print the UNIX errno value
*  We must append it to the end of the e,mesgstr[] array.
*/
my_perror()
{
   register int len;
   char         *sys_err_str();
   len = strlen(emesgstr);
   sprintf(emesgstr + len, " %s", sys_err_str());
}

extern int errno;           /* UNIX error number */
extern int sys_nerr;        /* # of error message strings in sys table */
extern char *sys_errlist[]; /* the system error message table */

/* Return a string containing sme additional operating-system
* dependet information.
*/
char *sys_err_str()
{
   static char msgstr[200];
   if (errno != 0 ){
      if (errno > 0 && errno < sys_nerr)
         sprintf(msgstr,"(%s)", sys_errlist[errno]);
      else
         sprintf(msgstr,"(errno = %d)", errno);
    }
    else msgstr[0] = '\0';
    return(msgstr);
}

#define syslog(a,b) fprintf(stderr,"%s\n", (b))      
err_sys( errortext )
char *errortext;
{
   fprintf( stderr,"%s\n", errortext );
   my_perror();
   syslog(LOG_ERR, emesgstr);
   return( 1 );
}

err_dump( errortext )
char *errortext;
{
   fprintf( stderr,"%s\n", errortext );
   my_perror();
   syslog(LOG_ERR, emesgstr);
   return( 1 );
}
/*
*******************************************************************************
* Read "n" bytes from a descriptor.
* Use in place of read() when fd is a stream socket.
*******************************************************************************
*/
int readn(fd, ptr, nbytes )
register int fd;
register char *ptr;
register int nbytes;
{
   int nleft, nread;
   nleft = nbytes;
   while (nleft > 0){
    nread = read(fd, ptr, nleft);
    if (nread < 0)
        return(nread); /* error, return < 0 */
    else if (nread == 0)
        break; /* EOF */
    nleft -= nread;
    ptr   += nread;
}
return(nbytes - nleft); /* return >= 0 */
}
/*
*******************************************************************************
* Read a stream socket one line at a time, and write each line back
* to the sender.
*
* Return when the connection is terminated.
*******************************************************************************
*/
#define MAXLINE 512
str_echo(sockfd)
int sockfd;
{
   int n;
   char line[MAXLINE];
   char line2[MAXLINE];
   for ( ; ; ) {
   n = readline(sockfd, line, MAXLINE);
   if (n == 0)
      return; /* connection terminated */
   else if (n < 0)
           err_dump("str_echo: readline error");
   strcpy(line2, "TCP-Echo=>" );
   strcat( line2, line);
   /* puts(line2); Testausgabe */
   n = strlen(line2); 
   if (writen(sockfd, line2, n ) != n)
      err_dump("str_echo: writen error");
   }
}
/*
------------------------------------------------------------------------------
The following function is used by the three connection-oriented clients:
------------------------------------------------------------------------------
* Read the contents of the FILE *fp, write each line to the
* stream socket (to the server process), then read a line back from
* the socket and write it to the standard output.
*
* Return to caller when an EOF is encountered on the input file.
*/
#include <stdio.h>
#define MAXLINE 512
str_cli ( fp, sockfd)
register FILE *fp;
register int sockfd;
{
   int n;
   char sendline[MAXLINE], recvline[MAXLINE + 1];

   while (fgets(sendline, MAXLINE, fp) != NULL) {
      n = strlen(sendline);
      if (writen(sockfd, sendline, n) != n)
         err_sys("str_cli: writen error on socket");
      /*
      * Now read a line from the socket and write it to
      * our standard output.
      */
      n = readline(sockfd, recvline, MAXLINE);
      if (n < 0)
         err_dump("str_cli: readline error");
      fputs(recvline, stdout);
   }
   if (ferror (fp) )
      err_sys("str_cli: error reading file");
}
/*
------------------------------------------------------------------------------
The following function is used by the three connectionless servers. By passing
address of the actual socket address structure to this function, it works with
all protocol families. Since the size of the structure can differ between
protocol families also pass its size to this function, as it is needed for the
recvfrom system call.
------------------------------------------------------------------------------
* Read a datagram from a connectionless socket and write it back to
* the sender.
*
* We never return, as we never know when a datagram client is done.
*/
#include <sys/types.h>
#include <sys/socket.h>
#define MAXMESG 2048
dg_echo(sockfd, pcli_addr, maxclilen)
int sockfd;
struct sockaddr *pcli_addr; /* ptr to appropriate sockaddr XX structure */
int maxclilen;          /* sizeof(*pcli_addr) */
{
   int n, clilen;
   char mesg[MAXMESG];
   char mesg2[MAXMESG];

   for ( ; ; ){
      clilen = maxclilen;
      n = recvfrom(sockfd, mesg, MAXMESG, 0, pcli_addr, &clilen);
      if (n < 0)
         err_dump("dg_echo: recvfrom error");
      /* Protokollausgabe */
      mesg[n] = 0; /* String-Laenge begrenzen */
      printf("UDP-Server-recvfrom:%slng=%d\n", mesg, n );
      /* Manipulation der Zeilen */
      strcpy( mesg2, "UDP-Echo=>" );
      strcat( mesg2, mesg);
      n = strlen(mesg2);
      if (sendto(sockfd, mesg2, n, 0, pcli_addr, clilen) != n)
     err_dump("dg_echo: sendto error");
   }
}
/*
------------------------------------------------------------------------------
The following function is for the connectionless clients. It is similar to the
one for a connection-oriented client, with the writen calls replaced by sendto
and the readn calls replaced by recvfrom. Also, we need the address of the
actual socket address structure and its size for the datagram system calls.
------------------------------------------------------------------------------
* Read the contents of the FILE *fp, write each line to the
* datagram socket, then read a line back from the datagram
* socket and write it to the standard output.
*
* Return to caller when an EOF is encountered on the input file.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#define MAXLINE 512

dg_cli(fp, sockfd, pserv_addr, servlen)
FILE *fp; int sockfd;
struct sockaddr *pserv_addr;    /* ptr to appropriate sockaddr_XX structure */
int servlen;            /* actual sizeof(*pserv_addrj */
{
   int n;
   char sendline[MAXLINE], recvline[MAXLINE + 1];
   while (fgets(sendline, MAXLINE, fp) != NULL) {
      n = strlen(sendline);
      if (sendto(sockfd, sendline, n, 0, pserv_addr, servlen) != n)
         err_dump("dg_cli: sendto error on socket");
      /*
      * Now read a message from the socket and write it to
      * our standard output.
      */
      n = recvfrom(sockfd, recvline, MAXLINE, 0,
                           (struct sockaddr *) 0, (int *) 0);
      if (n < 0)
         err_dump("dg_cli: recvfrom error");
      recvline[n] = 0; /* null terminate */
      fputs(recvline, stdout);
   }

   if (ferror(fp))
   err_dump("dg_cli: error reading file");
}
/*
------------------------------------------------------------------------------
The following function writes to a stream socket:
------------------------------------------------------------------------------
* Write "n" bytes to a descriptor.
* Use in place of write() when fd is a stream socket. /*
*/
int writen(fd, ptr, nbytes)
register int fd;
register char *ptr;
register int nbytes;
{
   int nleft, nwritten;
   nleft = nbytes;
   while (nleft > 0) {
      nwritten = write(fd, ptr, nleft);
      if (nwritten <= 0)
         return(nwritten); /* error */
      nleft -= nwritten;
      ptr   += nwritten;
   }
   return(nbytes - nleft);
}
/*
------------------------------------------------------------------------------
We use the following function to read a line from a stream socket. In our
examples we'll be exchanging Unix text lines between the client and server.
------------------------------------------------------------------------------
* Read a line from a descriptor. Read the line one byte at a time,
* looking for the newline. We store the newline in the buffer,
* then follow it with a null (the same as fgets(3)).
* We return the number of characters up to, but not including,
* the null (the same as strlen(3)).
*/
int readline(fd, ptr, maxlen)
register int fd;
register char *ptr;
register int maxlen;
{
   int n, rc;
   char c;
   for (n = 1; n < maxlen; n++) {
      if ( (rc = read(fd, &c, 1)) == 1){
         *ptr++ = c;
         if (c == '\n')
            break;
      }
      else if (rc == 0) {
              if (n == 1)
                 return(0); /* EOF, no data read */
              else
                 break; /* EOF, some data was read */
           }
           else
               return(-1); /* error */
   }
   *ptr = 0;
   return(n);
}

, если я пытаюсь сделать echoDUPclient, я получаю следующую ошибку:

cc -g -lnsl     echoUDPclient.c   -o echoUDPclient
/tmp/cclsBda6.o: In function `main':
/home/hannes/Dokumente/0_university/20_netzwerkprogrammierung/echo/echoUDP/echoUDPclient.c:81: undefined reference to `err_dump'
/home/hannes/Dokumente/0_university/20_netzwerkprogrammierung/echo/echoUDP/echoUDPclient.c:90: undefined reference to `err_dump'
/home/hannes/Dokumente/0_university/20_netzwerkprogrammierung/echo/echoUDP/echoUDPclient.c:92: undefined reference to `dg_cli'

с помощью утилит make я получаю:

cc -g -lnsl  -c utilities.c 
utilities.c:23:14: Fehler: In Konflikt stehende Typen für »sys_errlist«
/usr/include/i386-linux-gnu/bits/sys_errlist.h:28:30: Anmerkung: Vorherige Deklaration von »sys_errlist« war hier

(означает конфликт с sys_errlist и предыдущим объявлением в sys_errlist.h)

В Solaris это шолуд, но не в моем linux,Так что же я должен изменить?

1 Ответ

2 голосов
/ 27 марта 2012

Ваш первый вызов cc жалуется на неопределенные ссылки, так как вы не указали компоновщику, где найти объектный файл utilities.o. Скажите компоновщику (добавив utilities.o к вызову cc) или просто придерживайтесь Makefile.

Согласно моим руководствам по Linux, sys_errlist имеет тип char **, а вы определяете char *sys_errlist[] в utilities.c. Стоит отметить, что sys_errlist устарело, и вместо него следует использовать strerror().

Это дает вам как минимум два варианта:

  • либо исправьте ваше определение sys_errlist, например полностью удалив его и полагаясь на stdio.h, чтобы правильно объявить его
  • заменить весь sys_errlist материал на правильное strerror() сообщение об ошибке

В sidenote может быть проще придумать свой собственный клиент / сервер UDP echo вместо портирования реализации Solaris. Кроме того, вы должны правильно пометить ваши домашние вопросы как таковые.

Grüße aus dem Saarland:)

...