Commits

Ivan Vučica committed 078e26d

Added a readme and libjingle0.3.0-p2ptest

Comments (0)

Files changed (2)

+libjingle-test
+==============
+
+These are a few small test files that basically do nothing more than document my experiments with libjingle. They are somewhat a documentation of my failures more than anything useful, but I'm still putting them out there in case someone is interested in understanding libjingle. 
+
+<ivan@vucica.net>
+
+libjingle0.3.0-test.cpp
+-----------------------
+Attempt to get the following tutorial for libjingle to work:
+<http://maemo.org/development/documentation/manuals/3-x/howto_use_stun_bora/>
+
+However, I realized I had a too new version of libjingle. This was the first piece of code I pasted onto my machine that was supposed to interact with libjingle, but I never actually ran this.
+
+libjingle-test.cpp
+------------------
+Experimental code for compiling with revision 58. Got nowhere.
+
+libjingle-p2ponly.cpp
+---------------------
+More experimental code for compiling with revision 58. Attempted to explore the guts of libjingle by attempting to circumvent XMPP-based signaling code, in order to figure out how to use only libjingle's NAT traversal and session establishment code by implementing my own signaling. Intention was to get my custom non-audio and non-video data, using my own signaling, to another peer.
+
+I ended up concluding that libjingle's internals, at that time, didn't expose enough and required too much copypasting in order to do what I wanted to do.

libjingle0.3.0-test.cpp

+//#define POSIX
+#define POSIX 1
+//#define SIGSLOT_USE_POSIX_THREADS
+#define SIGSLOT_USE_POSIX_THREADS 1
+
+#include <libjingle/talk/base/thread.h>
+
+#include <libjingle/talk/base/network.h>
+#include <libjingle/talk/base/socketaddress.h>
+#include <libjingle/talk/base/physicalsocketserver.h>
+#include <libjingle/talk/p2p/base/sessionmanager.h>
+//#include <libjingle/talk/p2p/base/helpers.h>
+#include <libjingle/talk/p2p/client/basicportallocator.h>
+
+//#include <libjingle/talk/p2p/client/socketclient.h>
+#include <libjingle/talk/p2p/client/socketmonitor.h>
+#define SocketClient cricket::SocketMonitor
+namespace cricket { class Thread : public talk_base::Thread {}; } 
+
+#include <string>
+#include <vector>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <signal.h>
+
+void signaling_init(const char* ip, unsigned int port);
+int signaling_wait(unsigned int timeout);
+void signaling_sendall(const char* buffer, unsigned int len);
+
+SocketClient* socketclient_init(const char *stun_ip, unsigned int stun_port, 
+						const char *turn_ip, unsigned int turn_port);
+
+char* socketclient_add_remote_candidates(SocketClient *sc, char* buffer); 
+void socketclient_add_remote_candidate(SocketClient *sc, const char *candidate);
+
+bool socketclient_is_writable (SocketClient *sc);
+
+void socketclient_send(SocketClient *sc, const char *data, unsigned int len);
+
+void randomize();
+
+///
+bool p2p_state = false;
+int signaling_socket = -1;
+
+cricket::Thread *main_thread = 0;
+
+///
+
+int main(int argc, char* argv[])
+{
+	signal(SIGPIPE, SIG_IGN);
+	randomize();
+	
+	// P2P signaling server
+	const char* signaling_ip = "200.184.118.140";
+	int signaling_port = 14141;
+
+	// STUN server if any, set to NULL if there are none
+	const char* stun_ip = "200.184.118.140";
+	// const char* stun_ip = 0;
+	unsigned int stun_port = 7000;
+
+	// TURN server if any, set to NULL if there are none
+	const char* turn_ip = "200.184.118.140";
+	// const char* turn_ip = 0;
+	unsigned int turn_port = 5000;
+
+	signaling_init(signaling_ip, signaling_port);
+
+	SocketClient* sc = socketclient_init(stun_ip, stun_port, turn_ip, turn_port);
+	
+        sc->getSocketManager()->StartProcessingCandidates();
+
+	while (1) {
+		char buffer[10000];
+		char *buffer_p = buffer;
+		char *buffer_interpreted = buffer;
+
+		while (! p2p_state) {
+			main_thread->Loop(10);
+	
+			if (! signaling_wait(1)) {
+				printf("-- tick --\n");
+				continue;
+			}
+	
+			int n = recv(signaling_socket, buffer_p, sizeof(buffer) - (buffer_p - buffer), 0);
+			if (n < 0) {
+				printf("Signaling socket closed with error\n");
+				exit(1);
+			} else if (n == 0) {
+				printf("Signaling socket closed\n");
+				exit(1);
+			}
+			buffer_p += n;
+			buffer_interpreted = socketclient_add_remote_candidates(sc, buffer_interpreted);
+		}
+	
+		// P2P connection is up by now.
+		
+		while (p2p_state) {
+			// sends a byte via P2P connection
+			unsigned char data = random() % 256;
+			socketclient_send(sc, (char*) &data, 1);
+			sleep(random() % 15 + 1);
+			main_thread->Loop(10);
+		}
+
+		// P2P connection is broken, restart handling connection candidates
+	}
+}
+
+///
+
+void randomize()
+{
+	struct timeval tv;
+	struct timezone tz;
+	gettimeofday(&tv, &tz);
+	srandom(tv.tv_usec);
+}
+
+///
+
+void signaling_init(const char* ip, unsigned int port)
+{
+	struct sockaddr_in sa;
+
+	signaling_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	
+	bzero(&sa, sizeof(sa));
+	sa.sin_family = AF_INET;
+	sa.sin_port = htons(port);
+	inet_aton(ip, &(sa.sin_addr));
+
+	if (connect(signaling_socket, (struct sockaddr*) &sa, sizeof(sa)) < 0) {
+		printf("Error in signaling connect()\n");
+		exit(1);
+	}
+}
+
+////
+int signaling_wait(unsigned int timeout) {
+	fd_set rfds;
+	struct timeval tv;
+	int retval;
+
+	FD_ZERO(&rfds);
+	FD_SET(signaling_socket, &rfds);
+
+	tv.tv_sec = timeout;
+	tv.tv_usec = 0;
+
+	retval = select(signaling_socket+1, &rfds, NULL, NULL, &tv);
+	if (retval == -1)
+		printf("error in select()");
+
+	return (retval > 0);
+}
+
+///
+
+class SignalListener1 : public sigslot::has_slots<>
+{
+private:
+	SocketClient *sc;
+
+public:
+	SignalListener1(SocketClient *psc);
+	void OnCandidatesReady(const std::vector<Candidate>& candidates);
+	void OnNetworkError();
+	void OnSocketState(bool state);
+};
+
+class SignalListener2 : public sigslot::has_slots<>
+{
+private:
+	SocketClient *sc;
+
+public:
+	SignalListener2(SocketClient *psc);
+	void OnSocketRead(P2PSocket *socket, const char *data, size_t len);
+};
+
+SignalListener1::SignalListener1(SocketClient* psc)
+{
+	sc = psc;
+}
+
+SignalListener2::SignalListener2(SocketClient* psc)
+{
+	sc = psc;
+}
+
+void SignalListener1::OnNetworkError()
+{
+	printf ("Network error encountered at SocketManager");
+	exit(1);
+}
+
+///
+
+void SignalListener1::OnSocketState(bool state)
+{
+	printf("Socket state changed to %d\n", state);
+	p2p_state = state;
+	if (state) {
+		printf("Writable from %s:%d to %s:%d\n", 
+			sc->getSocket()->best_connection()->local_candidate().address().IPAsString().c_str(),
+			sc->getSocket()->best_connection()->local_candidate().address().port(),
+			sc->getSocket()->best_connection()->remote_candidate().address().IPAsString().c_str(),
+			sc->getSocket()->best_connection()->remote_candidate().address().port());
+	}
+}
+
+///
+
+SocketClient* socketclient_init(const char *stun_ip, unsigned int stun_port, 
+                                const char *turn_ip, unsigned int turn_port)
+{
+	cricket::SocketAddress *stun_addr = NULL;
+	if (stun_ip) {
+		stun_addr = new cricket::SocketAddress(std::string(stun_ip), stun_port);
+	}
+
+	cricket::SocketAddress *turn_addr = NULL;
+	if (turn_ip) {
+		turn_addr = new cricket::SocketAddress(std::string(turn_ip), turn_port);
+	}
+
+	cricket::PhysicalSocketServer *ss = new PhysicalSocketServer();
+	main_thread = new Thread(ss);
+	cricket::ThreadManager::SetCurrent(main_thread);
+
+	SocketClient *sc = new SocketClient (stun_addr, turn_addr);
+
+	// Note that signal connections pass the SignalListener1 object as well as the
+	// method. Since a new SocketListener1 is created for every new SocketClient,
+	// we have the guarantee that each SocketListener1 will be called back only
+	// in behalf of its related SocketClient.
+
+	sc->sigl1 = new SignalListener1(sc);
+	sc->sigl2 = new SignalListener2(sc);
+	sc->getSocketManager()->SignalNetworkError.connect(sc->sigl1, &SignalListener1::OnNetworkError);
+	sc->getSocketManager()->SignalState_s.connect(sc->sigl1, &SignalListener1::OnSocketState);
+	sc->getSocketManager()->SignalCandidatesReady.connect(sc->sigl1, &SignalListener1::OnCandidatesReady);
+  	sc->CreateSocket(std::string("foobar"));
+	sc->getSocket()->SignalReadPacket.connect(sc->sigl2, &SignalListener2::OnSocketRead); 
+
+	return sc;
+}
+
+
+///
+
+void SignalListener1::OnCandidatesReady(const std::vector<Candidate>& candidates)
+{
+	printf("OnCandidatesReady called with %d candidates in list\n", candidates.size());
+  	
+	for(std::vector<Candidate>::const_iterator it = candidates.begin(); it != candidates.end(); ++it) {
+		char *marshaled_candidate;
+
+		asprintf(&marshaled_candidate, "%s %d %s %f %s %s %s\n",
+			(*it).address().IPAsString().c_str(), (*it).address().port(), (*it).protocol().c_str(),
+    			(*it).preference(), (*it).type().c_str(), (*it).username().c_str(), 
+			(*it).password().c_str() );
+
+		printf("Candidate being sent: %s", marshaled_candidate);
+
+		signaling_sendall(marshaled_candidate, strlen(marshaled_candidate));
+		
+		free(marshaled_candidate);
+
+	}
+}
+
+///
+
+void signaling_sendall(const char* buffer, unsigned int len)
+{
+	unsigned int sent = 0;
+	while (sent < len) {
+		int just_sent;
+		just_sent = send(signaling_socket, buffer+sent, len-sent, 0);
+		if (just_sent < 0) {
+			printf("Signaling socket closed with error.\n");
+			exit(1);
+		} else if (just_sent == 0) {
+			printf("Signaling socket closed.\n");
+			exit(1);
+		}
+		sent += just_sent;
+	}
+}
+
+////
+
+// extracts remote candidates from a buffer, returns a pointer to the rest of the buffer
+
+char* socketclient_add_remote_candidates(SocketClient *sc, char* buffer)
+{
+	char *n;
+	char candidate[1024];
+
+	while (1) {
+		n = strchr(buffer, '\n');
+		if (! n) {
+			return buffer;
+		}
+		strncpy(candidate, buffer, n-buffer+1);
+		socketclient_add_remote_candidate(sc, candidate);
+		buffer = n+1;
+	}
+}
+
+
+////
+
+// Inform candidates received from the signaling network to LibJingle
+
+void socketclient_add_remote_candidate(SocketClient *sc, const char* remote_candidate)
+{
+	std::vector<Candidate> candidates;
+
+	char ip[100];
+	unsigned int port;
+	char protocol[100];
+	float preference;
+	char type[100];
+	char username[100];
+	char password[100];
+
+	// WARNING: using fixed-size buffers and sscanf is utterly unsafe. 
+	// Real implementations must be more robust about data coming from the network!
+
+	sscanf(remote_candidate, "%s %d %s %f %s %s %s\n", ip, &port, protocol, &preference, type, username, password);
+
+	printf("Received new candidate: %s:%d pref %f\n", ip, port, preference);
+
+	Candidate candidate;
+	candidate.set_name("rtp");
+	candidate.set_address(SocketAddress(std::string(ip), port));
+	candidate.set_username(std::string(username));
+	candidate.set_password(std::string(password));
+	candidate.set_preference(preference);
+	candidate.set_protocol(protocol);
+	candidate.set_type(type);
+	candidate.set_generation(0);
+	
+	candidates.push_back(candidate);
+
+	sc->getSocketManager()->AddRemoteCandidates(candidates);
+}
+
+
+////
+
+bool socketclient_is_writable(SocketClient *sc)
+{
+	return sc->getSocketManager()->writable();
+}
+
+////
+void SignalListener2::OnSocketRead(P2PSocket *socket, const char *data, size_t len)
+{
+	printf("Received byte %d from remote P2P\n", data[0]);
+}
+
+
+////
+void socketclient_send(SocketClient* sc, const char *data, unsigned int len)
+{
+	sc->getSocket()->Send(data, len);
+	printf("Sent byte %d to remote P2P\n", data[0]);
+}