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.oHier 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