Netzwerkprogrammierung mit C++ in Verbindung mit Quellcodes von "Programmieren von UNIX-Netzwerken"



  • Hallo Community,

    ich möchte gerne Netzwerkprogrammierung lernen und habe mir dafür das Buch "Programmieren von UNIX-Netzwerken" von Stevens (deutsche Fassung) ausgeliehen.

    Hat mit diesem Buch und dem Quellcode Erfahrung? Denn ich bräuchte da mal einen fachmännischen Rat und Hilfe.

    Ich habe die Sourcecodes von hier geladen und die make-Befehle und Konfiguration soweit ausgeführt.

    Ich habe mir in dem Verzeichnis "unp13e", also dort, wo auch alle Ordner der Kapitel sind, einen Ordner angelegt, um selbst ein wenig zu programmieren und die Header-Datei unp.h zu benutzen. Ich habe mir auch eine Make-Datei geschrieben, so wie sie auch in den anderen Ordnern vorhanden ist.

    Jetzt ist das Problem:
    Wenn mein Programm die Endung .c hat und ich kompiliere, funtkioniert alles. Ist es aber C-Code und endet auf .cpp und ich möchte kompilieren (gleicher Make-Befehl), kommen Fehler, welche ich gleich beschreiben werde.
    Ob ich gcc oder g++ verwende, spielt keine Rolle.
    Ist es möglich, mit den Quellcodes von Stevens auch in C++ zu programmieren? Das würde ich nämlich eher bevorzugen...

    Mein kleines Testprogramm sieht so aus:

    #include "unp.h"
    #include <time.h>
    
    int main(int argc, char** argv) {
    
    	int listenfd, connfd;
    	struct sockaddr_in servaddr;
    	char buff[MAXLINE];
    	time_t ticks;
    
    	listenfd = Socket(AF_INET, SOCK_STREAM, 0);
    	bzero(&servaddr, sizeof(servaddr));
    
    	servaddr.sin_family= AF_INET;
    	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    	servaddr.sin_port = htons(13);
    
    	Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
    
    	Listen(listenfd, LISTENQ);
    
    	for ( ; ;) {
    		connfd = Accept(listenfd, (SA *) NULL, NULL);
    
    		ticks=time(NULL);
    
    		snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
    		Write(connfd, buff, strlen(buff));
    
    		Close(connfd);
    	}
    		return 0;
    }
    

    Also nichts besonderes. Die Methoden sind groß geschrieben, da ich die Wrappermethoden von Stevens benutze, so wie er es auch tut.

    Meine make-Dateien sehen so aus:

    # Makefile "testnetwork"
    include ../Make.defines
    
    PROGS = server
    
    all: $(PROGS)
    
    server:		server.o
    	${CXX} ${CFLAGS} -o $@ server.o $(LIBS)
    

    Und die inkludierte Make.defines (von Stevens im Paket dabei) so:

    #
    # This file is generated by autoconf from "Make.defines.in".
    #
    # This is the "Make.defines" file that almost every "Makefile" in the
    # source directories below this directory include.
    # The "../" in the pathnames actually refer to this directory, since
    # "make" is executed in all the subdirectories of this directory.
    #
    # System = x86_64-unknown-linux-gnu
    
    CC = gcc
    CXX = g++
    CFLAGS = -I../lib/ -g -O2 -D_REENTRANT -Wall
    LIBS = ../libunp.a -lpthread
    LIBS_XTI = ../libunpxti.a ../libunp.a -lpthread
    RANLIB = ranlib
    
    # Following is the main library, built from all the object files
    # in the lib/ and libfree/ directories.
    LIBUNP_NAME = ../libunp.a
    
    # Following is the XTI library, built from all the object files
    # in the libxti/ directory.
    LIBUNPXTI_NAME = ../libunpxti.a
    
    # Following are all the object files to create in the lib/ directory.
    LIB_OBJS =  connect_nonb.o connect_timeo.o daemon_inetd.o daemon_init.o dg_cli.o dg_echo.o error.o get_ifi_info.o gf_time.o host_serv.o family_to_level.o mcast_leave.o mcast_join.o mcast_get_if.o mcast_get_loop.o mcast_get_ttl.o mcast_set_if.o mcast_set_loop.o mcast_set_ttl.o my_addrs.o read_fd.o readline.o readn.o readable_timeo.o rtt.o signal.o signal_intr.o sock_bind_wild.o sock_cmp_addr.o sock_cmp_port.o sock_ntop.o sock_ntop_host.o sock_get_port.o sock_set_addr.o sock_set_port.o sock_set_wild.o sockfd_to_family.o str_cli.o str_echo.o tcp_connect.o tcp_listen.o tv_sub.o udp_client.o udp_connect.o udp_server.o wraplib.o wrapsock.o wrapstdio.o wrappthread.o wrapunix.o write_fd.o writen.o writable_timeo.o
    
    # Following are all the object files to create in the libfree/ directory.
    LIBFREE_OBJS =  in_cksum.o inet_ntop.o inet_pton.o
    
    # Following are all the object files to create in the libgai/ directory.
    LIBGAI_OBJS = 
    
    # Following are all the object files to create in the libroute/ directory.
    LIBROUTE_OBJS = get_rtaddrs.o if_indextoname.o if_nameindex.o if_nametoindex.o net_rt_iflist.o net_rt_dump.o sock_masktop.o
    
    # Following are all the object files to create in the libxti/ directory.
    LIBXTI_OBJS =  wrapxti.o xti_accept.o xti_flags_str.o xti_getopt.o xti_ntop.o xti_ntop_host.o xti_rdwr.o xti_setopt.o xti_str_opts.o xti_tlook_str.o
    
    CLEANFILES = core core.* *.core *.o temp.* *.out typescript* \
    		*.lc *.lh *.bsdi *.sparc *.uw
    

    Die make-Ausgabe mit der C-Datei:

    gcc -I../lib/ -g -O2 -D_REENTRANT -Wall   -c -o server.o server.c          
    g++ -I../lib/ -g -O2 -D_REENTRANT -Wall -o server server.o ../libunp.a -lpthread
    

    Und die Make-Ausgabe mit der Cpp-Datei:

    g++    -c -o server.o server.cpp                                           
    server.cpp:8:17: error: unp.h: No such file or directory
    server.cpp: In function 'int main(int, char**)':
    server.cpp:14: error: aggregate 'sockaddr_in servaddr' has incomplete type and cannot be defined
    server.cpp:15: error: 'MAXLINE' was not declared in this scope
    server.cpp:18: error: 'AF_INET' was not declared in this scope
    server.cpp:18: error: 'SOCK_STREAM' was not declared in this scope
    server.cpp:18: error: 'Socket' was not declared in this scope
    server.cpp:19: error: 'bzero' was not declared in this scope
    server.cpp:22: error: 'INADDR_ANY' was not declared in this scope
    server.cpp:22: error: 'htonl' was not declared in this scope
    server.cpp:23: error: 'htons' was not declared in this scope
    server.cpp:25: error: 'SA' was not declared in this scope
    server.cpp:25: error: expected primary-expression before ')' token
    server.cpp:25: error: 'Bind' was not declared in this scope
    server.cpp:27: error: 'LISTENQ' was not declared in this scope
    server.cpp:27: error: 'Listen' was not declared in this scope
    server.cpp:30: error: expected primary-expression before ')' token
    server.cpp:30: error: 'Accept' was not declared in this scope
    server.cpp:34: error: 'buff' was not declared in this scope
    server.cpp:34: error: 'snprintf' was not declared in this scope
    server.cpp:35: error: 'strlen' was not declared in this scope
    server.cpp:35: error: 'Write' was not declared in this scope
    server.cpp:37: error: 'Close' was not declared in this scope
    make: *** [server.o] Error 1'
    

    Es macht übrigens keinen Unterschied bei der Ausgabe, ob ich nun mit CC oder CXX kompiliere.

    Ich hoffe, jemand kann hier helfen.



  • Das entscheidende: server.cpp:8:17: error: unp.h: No such file or directory

    und nun schau mal wie sich
    g++ -c -o server.o server.cpp
    und
    gcc -I../lib/ -g -O2 -D_REENTRANT -Wall -c -o server.o server.c
    unterscheiden und wo die unp.h liegt.

    Achso für C++ werden bei make die CXXFLAGS benutzt.



  • Ja, das ist merkwürdig.

    Ich ändere nichts am make-Aufruf, der make Aufruf ist immer

    ${CXX} ${CFLAGS} -o $@ server.o $(LIBS)
    

    Wobei CFLAGS in Make.defines gesetzt wurde mit

    CFLAGS = -I../lib/ -g -O2 -D_REENTRANT -Wall
    

    und LIBS definiert wurde mit

    LIBS = ../libunp.a -lpthread
    

    Also mit der C-Datei werden auch die Variablen sinnvoll aufgelöst (wobei ich sehe, dass das POroblem bei der Erstellung der .o-Datei liegt)

    Kompiliere ich die cpp-Datei manuell mit

    g++ -I../lib/ -g -O2 -D_REENTRANT -Wall -o server server.cpp ../libunp.a -lpthread
    

    kommt die Fehlermeldung:

    /tmp/ccR292w9.o: In function `main':
    /home/lumbeck/Cworkspace/unpv13e/network/server.cpp:18: undefined reference to `Socket(int, int, int)'
    /home/lumbeck/Cworkspace/unpv13e/network/server.cpp:25: undefined reference to `Bind(int, sockaddr const*, unsigned int)'
    /home/lumbeck/Cworkspace/unpv13e/network/server.cpp:27: undefined reference to `Listen(int, int)'
    /home/lumbeck/Cworkspace/unpv13e/network/server.cpp:30: undefined reference to `Accept(int, sockaddr*, unsigned int*)'
    /home/lumbeck/Cworkspace/unpv13e/network/server.cpp:35: undefined reference to `Write(int, void*, unsigned long)'
    /home/lumbeck/Cworkspace/unpv13e/network/server.cpp:37: undefined reference to `Close(int)'
    collect2: ld returned 1 exit status
    

    Die Datei unp.h steht im Überverzeichnis im Ordner lib. also ../lib.
    Aber ob ich den Pfad beim Inkludieren angebe oder nicht, ändert nichts.

    EDIT:

    Den letzten Satz nicht gelesen, ich werde mal die CXXFLAGS setzen und melden, was passiert.

    EIDT2:

    Also mit den gesetzten CXXFLAGS, welche genau so sind wie CFLAGS (wenn ich CFLAGS in den Aufruf schreibe, warum wird diese dann einfach ognoriert, auch wenn ich g++ benutze?)

    mit manuellen Kompilieren:

    Error genau wie g++ -I../lib/ -g -O2 -D_REENTRANT -Wall   -c -o server.o server.cpp
    g++ -I../lib/ -g -O2 -D_REENTRANT -Wall -o server server.o ../libunp.a -lpthread
    server.o: In function `main':
    /home/lumbeck/Cworkspace/unpv13e/network/server.cpp:18: undefined reference to `Socket(int, int, int)'
    /home/lumbeck/Cworkspace/unpv13e/network/server.cpp:25: undefined reference to `Bind(int, sockaddr const*, unsigned int)'
    /home/lumbeck/Cworkspace/unpv13e/network/server.cpp:27: undefined reference to `Listen(int, int)'
    /home/lumbeck/Cworkspace/unpv13e/network/server.cpp:30: undefined reference to `Accept(int, sockaddr*, unsigned int*)'
    /home/lumbeck/Cworkspace/unpv13e/network/server.cpp:35: undefined reference to `Write(int, void*, unsigned long)'
    /home/lumbeck/Cworkspace/unpv13e/network/server.cpp:37: undefined reference to `Close(int)'
    collect2: ld returned 1 exit status
    make: *** [server] Error 1
    

    Das heißt also: Die Methoden sind bekannt, aber nicht definiert?

    Da frage ich mich, warum die Methoden im C-File bekannt sind.



  • Vermutlich fehlt ein extern "C" { ... } im Header. Das braucht man wegen dem name-Mangeling in C++. Mit man: nm kannst du ansonsten nachschauen welche Symbole in libunp.a definiert sind.



  • Ah, okay.
    Habe mal das nm auf die libunp.a angewendet. Das .o-File, welches die Methoden implementiert, ist wrapsock.o

    Hier mal dazu die nm-Ausgabe:

    wrapsock.o:
    00000000000004e0 T Accept
    00000000000004b0 T Bind  
    0000000000000480 T Connect
    0000000000000450 T Getpeername
    0000000000000420 T Getsockname
    00000000000003f0 T Getsockopt 
    0000000000000360 T Inet6_rth_add
    00000000000002d0 T Inet6_rth_getaddr
    0000000000000390 T Inet6_rth_init   
    0000000000000330 T Inet6_rth_reverse
    0000000000000300 T Inet6_rth_segments
    00000000000003c0 T Inet6_rth_space   
    0000000000000260 T Listen            
    0000000000000240 T Poll              
    0000000000000210 T Recv              
    00000000000001e0 T Recvfrom          
    00000000000001b0 T Recvmsg           
    0000000000000190 T Select            
    0000000000000160 T Send              
    00000000000000d0 T Sendmsg           
    0000000000000130 T Sendto            
    00000000000000a0 T Setsockopt        
    0000000000000070 T Shutdown          
    0000000000000050 T Sockatmark        
    0000000000000030 T Socket            
    0000000000000000 T Socketpair        
                     U __errno_location  
                     U accept            
                     U bind              
                     U connect           
                     U err_quit          
                     U err_sys           
                     U getenv            
                     U getpeername       
                     U getsockname       
                     U getsockopt        
                     U inet6_rth_add     
                     U inet6_rth_getaddr 
                     U inet6_rth_init    
                     U inet6_rth_reverse 
                     U inet6_rth_segments
                     U inet6_rth_space   
                     U listen            
                     U poll              
                     U recv              
                     U recvfrom          
                     U recvmsg           
                     U select            
                     U send              
                     U sendmsg           
                     U sendto            
                     U setsockopt        
                     U shutdown          
                     U sockatmark        
                     U socket            
                     U socketpair        
                     U strtol
    

    T bedeutet Global text symbol und U ist undefined symbol.

    Also sind die Methoden Socket, Bind etc doch alle definiert?



  • die Posix Funktionen heißen: socket, bind, listen, accept, write, close. In C sind fa() und Fa() zwei unterschiedliche Funktionen.



  • Das ist mir bekannt. Die Funktionen Socket etc sind Wrapper-Funktionen aus dem Buch. Sie rufen lediglich die Funktionen "socket" etc auf.
    So sehen diese aus:

    void
    Connect(int fd, const struct sockaddr *sa, socklen_t salen)
    {
    	if (connect(fd, sa, salen) < 0)
    		err_sys("connect error");
    }
    

    Diese sind in libunp.a definiert, wie ich per nm herausgefunden habe. Trotzdem werden sie bei cpp Files nicht erkannt und bei .c-Files aber schon. Da liegt ja das Problem.



  • wie gesagt: Vermutlich fehlt das extern "C"

    http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html#faq-32.4


Log in to reply