简易代理的测试

      网络摄像头使用mjpg-streamer实现,其内部的httpd.c实现了简易的http服务器。(没做特殊处理,内网)客户端可以使用网址形式访问摄像头数据。

      现有要求如何实现外网访问?看了一些,p2p,都是同一个原理,“打洞”。网上有相关的API,及现成的DEMO,但要做成自己的DEMO,还要看那些别人写的(C++)API(只有简单的流程介绍,新手要利用这些API写出个APP来,有难度)。

      本来想根据打洞原理写个简单的p2p(中间人)服务器,可想着想着就转到了代理去了。

      原先,服务器建立两个sock,分别绑定两个端口; mjpg-streamer连接其中一个,登录服务器,同时使用keepalive心跳包保持联系,同时再创建一个socket,等待客户端连接;客户端连接服务器的另一个端口,登录服务器,获取 mjpg-streamer的地址、端口信息,也使用使用keepalive心跳包保持联系,之后与 mjpg-streamer建立连接。

      后来写着写着,就成了:

      首先服务器建立两个sock,分别绑定两个端口(一个8080,与mjpg-streamer相同,和客户端交互, )(一个8081,与mjpg-streamer交互)。

      mjpg-streamer连接8081,在服务器上登录,然后等待。

      客户端连接8080,(客户端为浏览器,)发送请求。

      服务器接收到后转发给mjpg-streamer,同时将mjpg-streamer的响应传递给客户端。

     

服务器:

int main(void)
{
	socklen_t optival = 1;
	struct sockaddr_in addr;
	int res;
	socklen_t len;
	int cur_num;

	signal(SIGINT, sig_handler);
	
	cli_num = 0;

	childpid = vfork();
	if(0 == childpid) {
		/* let child use parent's space, but the atpid is setted to value 0 by child */
		/* so must set it to child's pid */
		childpid = getpid();

		signal(SIGUSR2, server_sig_handler);

		/* set socket to wait httpserver to connect */
		portfd = socket(AF_INET, SOCK_STREAM, 0);
		setsockopt(portfd, SOL_SOCKET, SO_REUSEADDR, &optival, sizeof(optival));
		if(-1 == portfd) {
			perror("/PORT/ socket");
			return -1;
		}
		fprintf(stderr, "/PORT/ create sockfd successfully !\n");

		addr.sin_family = AF_INET;
		addr.sin_port = htons(PORT);
		addr.sin_addr.s_addr = INADDR_ANY;	
			
		res = bind(portfd, (struct sockaddr*)&addr, sizeof(addr));
		if(-1 == res) {
			perror("/PORT/: bind");
			goto port_err;
		}
		fprintf(stderr, "/PORT/: bind sockfd successfully !\n");
		
		res = listen(portfd, MAXLINK);
		if(-1 == res) {
			perror("/PORT/: listen");
			goto port_err;		
		}
		fprintf(stderr, "/PORT/: listen sockfd from port %u successfully !\n", PORT);

		/* create thread to wait httpserver's connecting */
		do {
			res = pthread_create(&server.recvtid, NULL, httpserver_handler, (void*)portfd);
			if(res != 0) {
				fprintf(stderr, "/PORT/: pthread_create httpserver_handler failed !\n");
				perror("/PORT/: pthread_create");
			}
		} while(res);
		
		cur_num = 0;
		
		while(1) {
			
			proxy_sockfd = socket(AF_INET, SOCK_STREAM, 0);
			setsockopt(proxy_sockfd, SOL_SOCKET, SO_REUSEADDR, &optival, sizeof(optival));
			if(-1 == proxy_sockfd) {
				perror("/PROXY/: socket");
				continue;
			}
			fprintf(stderr, "/PROXY/: create sockfd successfully !\n");

			addr.sin_family = AF_INET;
			addr.sin_port = htons(PROXYPORT);
			addr.sin_addr.s_addr = INADDR_ANY;	
				
			res = bind(proxy_sockfd, (struct sockaddr*)&addr, sizeof(addr));
			if(-1 == res) {
				perror("/PROXY/: bind");
				goto err;
			}
			fprintf(stderr, "/PROXY/: bind sockfd successfully !\n");
			
			res = listen(proxy_sockfd, MAXLINK);
			if(-1 == res) {
				perror("/PROXY/: listen");
				goto err;		
			}
			fprintf(stderr, "/PROXY/: listen sockfd from port %u successfully !\n", PROXYPORT);
			
			fprintf(stderr, "/PROXY/: waitting for the data ...\n");
			
			cur_num = 0;

			while(1) {
				len = sizeof(struct sockaddr_in);
				cur_num = find_num();
				fprintf(stderr, "/PROXY/: current num of client: %d\n", cur_num);
				if(-1 == cur_num) {
					fprintf(stderr, "/PROXY/: [WARNING] The number of connections has reached the maxinum !!!\n");
					while((cur_num = find_num()) == -1);
				}

				p2pcli[cur_num].sockfd = accept(proxy_sockfd, (struct sockaddr*)&(p2pcli[cur_num].addr), &len);
				if(-1 == p2pcli[cur_num].sockfd) {
					perror("/PROXY/: accept");
					continue;
				}
				p2pcli[cur_num].flag = USED;

				res = pthread_create(&(p2pcli[cur_num].recvtid), NULL, httprecv_handler, (void*)p2pcli[cur_num].sockfd);
				if(res != 0) {
					fprintf(stderr, "/PROXY/: pthread_create port to recv failed !\n");
					close(p2pcli[cur_num].sockfd);
					p2pcli[cur_num].flag = EMPTY;
					continue;
				}

				res = pthread_create(&(p2pcli[cur_num].sendtid), NULL, httpsend_handler, (void*)p2pcli[cur_num].sockfd);
				if(res != 0) {
					fprintf(stderr, "/PROXY/: pthread_create port to send failed !\n");
					close(p2pcli[cur_num].sockfd);
					p2pcli[cur_num].flag = EMPTY;
					continue;
				}
			
				usleep(20);
			}	
port_err:
			close(server.sockfd);
			server.sockfd = -1;
err:
			close(proxy_sockfd);
		}
	} else {
		waitpid(childpid, NULL, 0);
	}

	return 0;
}
      首先创建子进程,而后等待子进程结束;子进程,建立一个与 mjpg-streamer通信的socket,创建一个线程等待mjpg-streamer连接。再建立一个与客户端通信的socket,为每一个客户端的发送、接收分别创建两个线程,不断做数据转发。

      测试情况:

      640x480    发送的buf只有1024时,浏览器接收到一两帧时候就开始不断弹出窗口;  发送的buf为1280x960时,图像比较流畅,和直接浏览的流畅性差不多,但时而会出现图像局部不正常现象。

      1024x768  发送的buf为1280x960时, 可以看到画面,但跳帧非常严重,同时也会不断弹出窗口。


      虽然这样的程序出来的效果非常差,但留个纪念吧。

你可能感兴趣的:(简易代理的测试)