博文

目前显示的是 五月, 2018的博文

P2P之UDP穿透的简单实现方式

NAT类型 Full Cone NAT:     内网主机建立一个UDPsocket(LocalIP:LocalPort) 第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP:PublicPort),以后用这个socket向外面任何主机发送数据都将使用这对(PublicIP:PublicPort)。此外,任何外部主机只要知道这个(PublicIP:PublicPort)就可以发送数据给(PublicIP:PublicPort),内网的主机就能收到这个数据包 Restricted Cone NAT:     内网主机建立一个UDPsocket(LocalIP:LocalPort) 第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP:PublicPort),以后用这个socket向外面任何主机发送数据都将使用这对(PublicIP:PublicPort)。此外,如果任何外部主机想要发送数据给这个内网主机,只要知道这个(PublicIP:PublicPort)并且内网主机之前用这个socket曾向这个外部主机IP发送过数据。只要满足这两个条件,这个外部主机就可以用自己的(IP,任何端口)发送数据给(PublicIP:PublicPort),内网的主机就能收到这个数据包 Port Restricted Cone NAT:      内网主机建立一个UDPsocket(LocalIP:LocalPort) 第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP:PublicPort),以后用这个socket向外面任何主机发送数据都将使用这对(PublicIP:PublicPort)。此外,如果任何外部主机想要发送数据给这个内网主机,只要知道这个(PublicIP:PublicPort)并且内网主机之前用这个socket曾向这个外部主机(IP,Port)发送过数据。只要满足这两个条件,这个外部主机就可以用自己的(IP,Port)发送数据给(PublicIP:PublicPort),内网的主机就能收到这个数据包 Symmetric NAT:  ...

使用TCP协议的NAT穿透技术

图片
一直以来,说起NAT穿透,很多人都会被告知使用UDP打孔这个技术,基本上没有人会告诉你如何使用TCP协议去穿透(甚至有的人会直接告诉你TCP协议是无法实现穿透的)。但是,众所周知的是,UDP是一个无连接的数据报协议,使用它就必须自己维护收发数据包的完整性,这常常会大大增加程序的复杂度,而且一些程序由于某些原因,必须使用TCP协议,这样就常常令一些开发TCP网络程序的人员“谈穿透色变”。那么,使用TCP协议是不是就不能实现穿透呢?答案当然是否定的:TCP协议不仅能实现NAT穿透,而且实现起来比UDP穿透甚至还简单一些。 要了解如何使用TCP穿透NAT,就要首先看看如何使用UDP穿透NAT。      我们假设在两个不同的局域网后面分别有2台客户机A和 B,AB所在的局域网都分别通过一个路由器接入互联网。互联网上有一台服务器S。      现在AB是无法直接和对方发送信息的,AB都不知道对方在互联网上真正的IP和端口, AB所在的局域网的路由器只允许内部向外主动发送的信息通过。对于B直接发送给A的路由器的消息,路由会认为其“不被信任”而直接丢弃。      要实现 AB直接的通讯,就必须进行以下3步:A首先连接互联网上的服务器S并发送一条消息(对于UDP这种无连接的协议其实直接初始会话发送消息即可),这样S就获取了A在互联网上的实际终端(发送消息的IP和端口号)。接着 B也进行同样的步骤,S就知道了AB在互联网上的终端(这就是“打洞”)。接着S分别告诉A和B对方客户端在互联网上的实际终端,也即S告诉A客户B的会话终端,S告诉B客户A的会话终端。这样,在AB都知道了对方的实际终端之后,就可以直接通过实际终端发送消息了(因为先前双方都向外发送过消息,路由上已经有允许数据进出的消息通道)。 用UDP来实现以上3步不存在什么理论上的问题,因为UDP是无连接的协议,它允许socket进行“多对一”的通讯(即几个具有不同IP和端口号的socket向一个接收socket发送消息)。但是使用TCP就出现了问题:在一般情况下,TCP socket不允许在已经建立连接的端口上再进行监听和使用该本地端口。换句话说,当AB连接上服务器S后,S将AB的实际终端告诉对方,...

TCP打洞与UDP打洞的区别

为什么网上讲到的P2P打洞基本上都是基于UDP协议的打洞?难道TCP不可能打洞?还是TCP打洞难于实现?     假设现在有内网客户端A和内网客户端B,有公网服务端S。     如果A和B想要进行UDP通信,则必须穿透双方的NAT路由。假设为NAT-A和NAT-B。          A发送数据包到公网S,B发送数据包到公网S,则S分别得到了A和B的公网IP, S也和A B 分别建立了会话,由S发到NAT-A的数据包会被NAT-A直接转发给A, 由S发到NAT-B的数据包会被NAT-B直接转发给B,除了S发出的数据包之外的则会被丢弃。 所以:现在A B 都能分别和S进行全双工通讯了,但是A B之间还不能直接通讯。     解决办法是:A向B的公网IP发送一个数据包,则NAT-A能接收来自NAT-B的数据包 并转发给A了(即B现在能访问A了);再由S命令B向A的公网IP发送一个数据包,则 NAT-B能接收来自NAT-A的数据包并转发给B了(即A现在能访问B了)。     以上就是“打洞”的原理。     但是TCP和UDP在打洞上却有点不同。这是因为伯克利socket(标准socket规范)的 API造成的。     UDP的socket允许多个socket绑定到同一个本地端口,而TCP的socket则不允许。      这是这样一个意思:A B要连接到S,肯定首先A B双方都会在本地创建一个socket, 去连接S上的socket。创建一个socket必然会绑定一个本地端口(就算应用程序里面没写 端口,实际上也是绑定了的,至少java确实如此),假设为8888,这样A和B才分别建立了到 S的通信信道。接下来就需要打洞了,打洞则需要A和B分别发送数据包到对方的公网IP。但是 问题就在这里:因为NAT设备是根据端口号来确定session,如果是UDP的socket,A B可以 分别再创建socket,然后将socket绑定到8888,这样打洞就成功了。但是如果是TCP的 socke...

安全

图片

Android学习地址

http://coderpig.cn

基于Libevent的HTTP Server

简单的Http Server 使用Libevent内置的http相关接口,可以很容易的构建一个Http Server,一个简单的Http Server如下: #include <event2/event.h> #include <event2/buffer.h> #include <event2/http.h> #include <Winsock2.h> #include <stdlib.h> #include <stdio.h> int init_win_socket() {     WSADATA wsaData;     if(WSAStartup(MAKEWORD(2,2) , &wsaData) != 0)      {         return -1;     }     return 0; } void generic_handler(struct evhttp_request *req, void *arg) {     struct evbuffer *buf = evbuffer_new();     if(!buf)     {         puts("failed to create response buffer \n");         return;     }     evbuffer_add_printf(buf, "Server Responsed. Requested: %s\n", evhttp_request_get_uri(req));     evhttp_send_reply(req, HTTP_OK, "OK", buf);     evbuffe...

libevent evhttp学习——http服务端

http服务端相对客户端要简单很多,我们仍旧使用libevent-2.1.5版本,服务端接口和2.0版本没有区别 基本流程 http服务端使用到的借口函数及流程如下 创建event_base和evhttp struct event_base * event_base_new ( void ) ; struct evhttp * evhttp_new (struct event_base *base) ; 绑定地址和端口 int evhttp_bind_socket (struct evhttp *http, const char *address, ev_uint16_t port) ; 设置处理函数 void evhttp_set_gencb (struct evhttp *http, void (*cb) (struct evhttp_request *, void *) , void *arg) ; 派发事件循环 int event_base_dispatch (struct event_base *) ; 完整代码 服务器接收到请求后打印URL,并返回一段文本信息 # include "event2/http.h" # include "event2/event.h" # include "event2/buffer.h" # include <stdlib.h> # include <stdio.h> void HttpGenericCallback (struct evhttp_request* request, void * arg) { const struct evhttp_uri * evhttp_uri = evhttp_request_get_evhttp_uri ( request ); char url[ 8192 ]; evhttp_uri_join( const_cast <struct evhttp_uri*>(evhttp_uri), url, 8192 ); printf ( ...

线程终止pthread_exit()&pthread_join()

linux下有两种方式可以使线程终止。第一种通过return从线程函数返回,第二种通过调用函数pthread_exit()是线程退出。 pthread_exit()函数原型: #include<pthread.h>  void pthread_exit(void* retval); 参数retval:pthread_exit()调用线程的返回值,可以用pthread_join()函数来检索获取。 功能:退出线程。 注意: 1.线程终止最重要的问题是资源释放的问题,特别是临界资源的释放。因为临界资源在一段时间内只能被一个线程所持有,当线程要使用临界资源时需提交请求,如果该资源未被使用则申请成功,否则等待。临界资源使用完毕后要释放以便其他线程可以使用。 2.线程终止的另外一个问题是线程间的同步问题。一般情况下,进程中各个线程的运行是相互独立的,线程的终止并不会相互通知,也不会影响其他的线程,终止的线程所占用的资源不会随着线程的终止而归还系统,而是仍为线程所在的进程持有。正如进程之间可以使用wait()系统调用来等待其他进程结束一样,线程也有类似的函数:pthread_join()函数。 pthread_join()函数原型: #include<pthread.h>    int pthread_join(pthread_t thread, void **retval); 参数: thread: 线程标识符,即线程ID,标识唯一线程。 retval: 用户定义的指针,用来存储被等待线程的返回值。 返回值 : 0代表成功。 失败,返回的则是错误号。 功能:以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果进程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。 注意: 1.一个可“join”的线程所占用的内存仅当有线程对其执行立pthread_join()后才会释放,因此为了避免内存泄漏,所有线程的终止时,要么已被设为DETACHED,要么使用pthread_join()来回收资源。 2.一个线程不能被多个线程等待,否则第一个接受到信号的线程成功返回,其余调用pthread_join()的线程返...