算法描述
随机选择两个大的素数 p、q ,且p ≠ q,计算n = pq、r = (p-1)(q-1),依欧拉定理,r即为与n互质的素数个数;选择一个小于r的整数e(即加密指数),求得e关于模r的逆元d(即解密指数),则{n,e}为公钥、{n,d}为私钥;根据模的逆元性质有ed ≡ 1 (mod r);设m为明文,则加密运算为m^e ≡ c (mod n), c即为密文;则解密过程 c^d ≡ m (mod n)。
证明会用到费马小定理,即
若y为素数且x不为y的倍数, 则 x^(y-1) ≡ 1 (mod y)(费马小定理的证明需先证明欧拉定理,此处略)。符号≡表示同余,^表示
幂,|表示整除,*表示相乘。
算法证明
第一种证明途径
因 ed ≡ 1 (mod (p-1)(q-1)),令 ed = k(p-1)(q-1) + 1,其中 k 是整数
则 c^d = (m^e)^d = m^(ed) = m^(k(p-1)(q-1)+1)
1.若m不是p的倍数,也不是q的倍数
则 m^(p-1) ≡ 1 (mod p) (
费马小定理)
=> m^(k(p-1)(q-1)) ≡ 1 (mod p)
m^(q-1) ≡ 1 (mod q) (
费马小定理)
=> m^(k(p-1)(q-1)) ≡ 1 (mod q)
故 p、q 均能整除 m^(k(p-1)(q-1)) - 1
=> pq | m^(k(p-1)(q-1)) - 1
即 m^(k(p-1)(q-1)) ≡ 1 (mod pq)
=> m^(k(p-1)(q-1)+1) ≡ m (mod n)
2.若m是p的倍数,但不是q的倍数
则 m^(q-1) ≡ 1 (mod q) (
费马小定理)
=> m^(k(p-1)(q-1)) ≡ 1 (mod q)
=> m^(k(p-1)(q-1)+1) ≡ m (mod q)
因 p | m
=> m^(k(p-1)(q-1)+1) ≡ 0 (mod p)
=> m^(k(p-1)(q-1)+1) ≡ m (mod p)
故 m^(k(p-1)(q-1)+1) ≡ m (mod pq)
即 m^(k(p-1)(q-1)+1) ≡ m (mod n)
3.若m是q的倍数,但不是p的倍数,证明同上
4.若m同为p和q的倍数时
则 pq | m
=> m^(k(p-1)(q-1)+1) ≡ 0 (mod pq)
=> m^(k(p-1)(q-1)+1) ≡ m (mod pq)
即 m^(k(p-1)(q-1)+1) ≡ m (mod n)
第二种证明途径
先证明m^ed ≡ m (mod p)恒成立
1.若p为m的因子,则p | m^ed - m显然成立,即m^ed ≡ m (mod p)
2.若p不为m的因子,令ed = k(p-1)(q-1) + 1,则 m^(ed-1) - 1 = m^(k(p-1)(q-1)) - 1
m^(p-1) ≡ 1 (mod p) (
费马小定理)
=> m^(k(p-1)) ≡ 1 (mod p)
=> m^(k(p-1)(q-1)) ≡ 1 (mod p)
=> m^(ed-1) ≡ 1 (mod p)
=> m^ed ≡ m (mod p)
同理可证m^ed ≡ m (mod q)
故m^ed ≡ m (mod pq),即m^ed ≡ m (mod n)
又因 c^d = m^e^d = m^(ed)
故 c^d ≡ m (mod n),证毕
总结
第二种比第一种简单直观,以上证明途径对RSA私钥签名与验签同样适合。
posted @
2016-11-18 17:05 春秋十二月 阅读(2620) |
评论 (0) |
编辑 收藏
脚本源码 由于很多应用项目依赖诸多第三方开源库,这些开源库各有不同的核心目录、库目标和输出位置,这里的核心目录是指
仅产生so库的工程目录,库目标是指
仅产生so库的make目标,输出位置是相对于核心目录的,但不必是子目录,可用..来回溯到父目录的某位置,更高层目录的位置,依次类推。为了统一支持它们,使用了一些技巧,详见示例脚本如下
1.PHONY: all clean lib core
2
3thirdlib=openssl-1.0.1u?build_ssl ACE_wrappers/ace json ncurses-6.0??lib
4coremod=main
5
6dir = `echo $@ | awk -F? '{print $$1}'`
7aim = `echo $@ | awk -F? '{print $$2}'`
8out = `echo $@ | awk -F? '{print $$3}'`
9
10copy=\cp -Pf ${dir}/${out}/*.so* output
11
12define MAKE_SUBDIR
13echo "${dir},${aim},${out}"; \
14if [ "$(MAKECMDGOALS)" != "clean" ]; then \
15$(MAKE) ${aim} -C ${dir}; \
16if [ "$$is_cp" -eq "1" ]; then \
17$(copy); \
18fi \
19else \
20$(MAKE) clean -C ${dir}; \
21fi
22endef
23
24all: lib core
25
26lib: $(thirdlib)
27
28$(thirdlib)::
29 @is_cp=1; $(MAKE_SUBDIR)
30
31core: $(coremod)
32
33$(coremod)::
34 @is_cp=0; $(MAKE_SUBDIR)
35
36clean: $(thirdlib) $(coremod) 实现技巧 1)使用?作为分隔符,所分隔的3个域依次为核心目录、库目标、输出位置;使用awk来获取各域,分别为dir、aim和out;在运行过程中,值dir一定非空,而aim为空则表示默认目标,out为空表示输出位置即为dir目录。
2)copy为命令变量,功能为每当一个库编译完成后,将输出的so库拷贝到output下,并保持软链接;对于有的开源库,需在编译前,使用对应的选项来调用configure,使其生成so库。
3)为了重用代码,定义了MAKE_SUBDIR命令包,参数变量为is_cp,当is_cp为1时,表示当前编译的是依赖库,否则是主程序。
4)thirdlib和coremod为依赖文件,使用了双冒号规则,这样一来,只要在thirdlib中加入新的依赖库,指定核心目录、库目标和输出位置即可,其它地方不用改。
posted @
2016-10-19 15:11 春秋十二月 阅读(3239) |
评论 (0) |
编辑 收藏
脚本概述 当需要在很多(比如几十至几百)台机器上编译同一程序时,如果一个个地手工拷贝源码、再编译,那么效率就很低,为了能大量节省手工、并行地编译,因此写了一个脚本,该脚本基于自动化脚本语言expect(expect基于tcl)实现,基本原理是针对每个远程主机,创建一个子进程,在该子进程内先调用scp拷贝源码到远程主机,再用ssh登录到远程主机、发送cd、configure和make命令,交互期间的命令输出多用正则分析,最终的编译输出保存到当前目录output子目录下。其命令行参数说明如下:
● 第1参数为远程主机配置文件:一个多行文本文件,每行格式为IP 用户名 密码,空格符分隔,支持#注释。
● 第2参数为本地主机源码目录:要求该目录存在Makefile和configure文件。
● 第3参数为远程主机目标目录:用于存放源码的位置。
脚本实现 拷贝源码
1proc copy_file {host user srcdir dstdir passwd {to 10} } {
2 if [catch "spawn scp -rq $srcdir $user@$host:$dstdir" msg] {
3 send_error "failed to spawn scp: $msg\n"
4 exit 1
5 }
6
7 set timeout $to
8 expect_after eof {
9 send_error "$host scp died unexpectedly\n"
10 exit 1
11 }
12 expect {
13 "(yes/no)?" { send "yes\r"; exp_continue }
14 -re "(?:P|p)assword:" { send "$passwd\r" }
15 timeout { do_timeout "$host scp" }
16 }
17
18 expect {
19 full_buffer { exp_continue }
20 timeout { exp_continue }
21 eof
22 }
23}
第2行调用spawn命令执行scp命令,并用catch捕捉错误;当执行成功后,第12行用expect等待远端输出(超时默认为10秒),第13、14行自动输入用户名和密码,当过程中网络连接断开时,会匹配到第8行的eof;当输出完成连接关闭时,会匹配到第21行的eof;如果输出太多超过expect内部的buffer时,会匹配到第19行的full_buffer,这里由于为了提高效率,使用了静默方式的scp,因些实际会匹配到第20行的timeout,不管匹配到哪种情况,都要继续直到eof。
执行编译
1proc do_make {host user passwd subdir {to 10} } {
2 if [catch {spawn ssh $user@$host} msg ] {
3 send_error "failed to spawn ssh: $msg\n"
4 exit 1
5 }
6
7 set timeout $to
8 expect_after eof {
9 send_error "$host ssh died unexpectedly\n"
10 exit 1
11 }
12
13 expect {
14 "*yes/no" { send "yes\r"; exp_continue }
15 -re "(?:P|p)assword:" { send "$passwd\r" }
16 timeout { do_timeout "$host ssh" }
17 }
18 wait_cmd $spawn_id passwd
19
20 send "cd $subdir\r"
21 wait_cmd $spawn_id cd
22
23 send "source configure\r"
24 wait_cmd $spawn_id configure
25
26 send "make\r"
27 wait_cmd $spawn_id make
28
29 send "exit\r"
30 expect eof
31}
关于spawn和expect的解释与上节
拷贝源码相同,不同的是依次发送命令cd、source configure、make,每个命令须等到命令提示符后(调用自定义函数wait_cmd)再发下一个,最后发送exit退出ssh、导致连接关闭,匹配到最后一行的eof。对于有的项目源码,可能没有或不用配置,那么configure文件可以不存在或内容为空,如果不存在导致报错也没关系,不影响make;如果configure出错,那么make也会出错。这里使用source是为了使配置在当前shell中生效。
主循环
1set f [open $file r]
2set curtime [clock seconds]
3
4log_user 0
5set s {[:blank:]}
6set pattern "^(\[^#$s]+)\[$s]+(\[^$s]+)\[$s]+(\[^$s]+)"
7
8while { [gets $f line] != -1 } {
9 if { ![regexp $pattern [string trimleft $line] ? host user passwd] } {
10 continue
11 }
12 send_user "$host $user $passwd\n"
13 if { ![fork] } {
14
15 set filename output/${host}_[clock format $curtime -format %y.%m.%d_%H.%M.%S].log
16 log_file -noappend -a $filename
17
18 copy_file $host $user $srcdir $dstdir $passwd 30
19 do_make $host $user $passwd $subdir 30
20
21 send_user "$host finish\n"
22 exit
23 }
24}
打开远程主机配置文件,读取每一行直到文件尾,忽略注释行,用正则提取IP、用户名和密码,创建子进程,按IP和当前时间命名log文件(由于前面调用log_user 0关闭了控制台输出,因此为了能记录输出到日志文件,一定要加-a选项),最后调用函数copy_file和do_make。
完整脚本下载:
autobuild.zip
posted @
2016-09-28 11:04 春秋十二月 阅读(3809) |
评论 (0) |
编辑 收藏
描述
拦截Linux动态库API的常规方法,是基于动态符号链接覆盖技术实现的,基本步骤是
1. 重命名要拦截的目标动态库。
2. 创建新的同名动态库,定义要拦截的同名API,在API内部调用原动态库对应的API。这里的同名是指与重命名前动态库前的名称相同。
显而易见,如果要拦截多个不同动态库中的API,那么必须创建多个对应的同名动态库,这样一来不仅繁琐低效,还必须被优先链接到客户二进制程序中(根据动态库链接原理,对重复ABI符号的处理是选择优先链接的那个动态库)。 另外在钩子函数的实现中,若某调用链调用到了原API,则会引起死循环而崩溃。本方法通过直接修改ELF文件中的动态库API入口表项,解决了常规方法的上述问题。
特点
1. 不依赖于动态库链接顺序。
2. 能拦截多个不同动态库中的多个API。
3. 支持运行时动态链接的拦截。
4. 钩子函数内的实现体,若调用到原API,则不会死循环。
实现
拦截映射表
为了支持特点2和3,建立了一个拦截映射表,这个映射表有2级。第1级为ELF文件到它的API钩子映射表,键为ELF文件句柄,值为API钩子映射表;第2级为API到它的钩子函数映射表,键为API名称,值为包含最老原函数地址和最新钩子函数地址的结构体,如下图
当最先打开ELF文件成功时,会在第1级映射表中插入记录;反之当最后关闭同一ELF文件时,就会从中移除对应的记录。当第一次挂钩动态库API时,就会在第2级映射表插入记录;反之卸钩同一API时,就会从中删除对应的记录。
计算ELF文件的映像基地址
计算映像基地址是为了得到ELF中动态符号表和重定位链接过程表的内容,因为这些表的位置都是相对于基地址的偏移量,该算法在打开ELF文件时执行,如下图
EXE文件为可执行文件,DYN文件为动态库。对于可执行文件,映射基地址为可执行装载段的虚拟地址;对于动态库,可通过任一API的地址减去它的偏移量得到,任一API的地址可通过调用libdl.so库API dlsym得到,偏移量通过查询动态链接符号表得到。
打开ELF文件
为了支持特点2即拦截不同动态库的多个API,节省每次挂钩API前要打开并读文件的开销,独立提供了打开ELF文件的接口操作,流程如下图
若输入ELF文件名为空,则表示打开当前进程的可执行文件,此时要从伪文件系统/proc/self/exe读取文件路径名,以正确调用系统调用open。当同一ELF文件被多次打开时,只须递增结构elf的引用计数。
挂钩API
当打开ELF文件后,就可挂钩API了,流程如下图
当第一次挂钩时,需要保存原函数以供后面卸钩;第二次以后继续挂钩同一API时,更新钩子函数,但原函数不变。
卸钩API
当打开ELF文件后,就可卸钩API了,流程如下图
关闭ELF文件
因为提供了打开ELF文件的接口操作,所以得配有关闭ELF文件的接口操作。当不需要挂钩API的时候,就可以关闭ELF文件了,流程如下图
运行时动态拦截装置
在初始化模块中打开当前可执行文件,挂钩libdl.so库的API dlopen和dlsym;在转换模块中,按动态库句柄和API名称在拦截映射表中查找钩子函数,若找到则返回钩子函数,否则返回调用dlsym的结果;在销毁模块中,卸钩dlopen和dlsym。
当动态库被进程加载的时候,会调用初始化模块;当被进程卸载或进程退出的时候,会调用销毁模块;当通过dlsym调用API时,则会在dlsym的钩子函数中调用转换模块。通过环境变量LD_PRELOAD将动态库libhookapi.so设为预加载库,这样就能拦截到所有进程对dlopen及dlsym的调用,进而拦截到已挂钩动态库API的调用。
posted @
2016-08-25 11:10 春秋十二月 阅读(2242) |
评论 (0) |
编辑 收藏
描述
云查杀平台以nginx作为反向代理服务器,作为安全终端与云查询服务的桥梁。当安全终端需要查询黑文件时,HTTP请求及其响应都会经过nginx,为了获取并统计一天24小时查询的黑文件数量,就得先截获经过nginx的HTTP响应,再做数据分析。截获HTTP数据流有多种方法,为了简单高效,这里使用了挂接HTTP过滤模块的方法,另外为了不影响nginx本身的IO处理,将HTTP响应实体发送到另一个进程即统计服务,由统计服务来接收并分析HTTP响应,架构如下图
统计服务由1个接收线程和1个存储线程构成,其中接收线程负责接收从nginx过滤模块发来的HTTP响应实体,解析它并提取黑文件MD5,加入共享环形队列;而存储线程从共享环形队列移出黑文件MD5,插入到临时内存映射文件,于每天定时同步到磁盘文件。
特点
这种架构减少了nginx IO延迟,保证了nginx的稳定高效运行,从而不影响用户的业务运行;本地连接为非阻塞的,支持了统计服务的独立运行与升级。
实现
nginx过滤模块
该流程运行在nginx工作进程。
由于nginx采用了异步IO机制,因此仅当截获到HTTP响应实体也就是有数据经过时,才有后面的操作;若没有数据,则什么也不用做。这里每次发送前先判断是否连接了统计服务,是为了支持统计服务的独立运行与升级,换句话说,不管统计服务是否运行或崩溃,都不影响nginx的运行。
统计服务
接收线程
这里的接收线程也就是主线程。
存储线程
存储线程为另一个工作线程。
同步文件定时器的时间间隔要比新建文件定时器的短,由于定时器到期的事件处理是一种异步执行流,所以将它们当做并行,与“从q头移出黑文件MD5”操作画在了同一水平方向。
posted @
2016-08-25 11:10 春秋十二月 阅读(1070) |
评论 (0) |
编辑 收藏
本方法适用于linux 2.6.x内核。
1. 先获取dentry所属文件系统对应的挂载点,基本原理是遍历文件系统vfsmount树,找到与dentry有相同超级块的vfsmount,实现如下
next_mnt函数实现了
先根遍历法,遍历以root为根的文件系统挂载点,p为遍历过程中的当前结点,返回p的下一个挂载点;vfsmnt_lock可通过内核函数kallsyms_on_each_symbol或kallsyms_lookup_name查找获得。
2. 再调用内核函数d_path,接口封装如下
posted @
2016-08-24 19:22 春秋十二月 阅读(5742) |
评论 (0) |
编辑 收藏
描述
原始套接字具有广泛的用途,特别是用于自定义协议(标准协议TCP、UDP和ICMP等外)的数据收发。在Linux下拦截套接字IO的一般方法是拦截对应的套接字系统调用,对于发送为sendmsg和sendto,对于接收为recvmsg和recvfrom。这种方法虽然也能拦截原始套接字IO,但要先判断套接字的类型,如果为SOCK_RAW(原始套接字类型),那么进行拦截处理,这样一来由于每次IO都要判断套接字类型,性能就比较低了。因此为了直接针对原始套接字来拦截,提高性能,发明了本方法。
本方法可用于防火墙或主机防护系统中,丢弃接收和发送的攻击或病毒数据包。
特点
运行在内核态,直接拦截所有进程的原始套接字IO,支持IPv4和IPv6。
实现
原理
在Linux内核网络子系统中,struct proto_ops结构提供了协议无关的套接字层到协议相关的传输层的转接,而IPv4协议族中内置的inet_sockraw_ops为它的一个实例,对应着原始套接字。因此先找到inet_sockraw_ops,再替换它的成员函数指针recvmsg和sendmsg,就可以实现拦截了。下面以IPv4为例(IPv6同理),说明几个流程。
搜索inet_sockraw_ops
该流程在挂钩IO前进行。由于inet_sockraw_ops为Linux内核未导出的内部符号,因此需要通过特别的方法找到它,该特别的方法基于这样的一个事实:
◆ 所有原始套接字接口均存放在以SOCK_RAW为索引的双向循环链表中,而inet_sockraw_ops就在该链表的末尾。
◆ 内核提供了注册套接字接口的API inet_register_protosw,对于原始套接字类型,该API将输入的套接字接口插入到链表头后面。
算法如下
注册p前或注销p后,链表如下
注册p后,链表如下
挂钩IO
该流程在内核模块启动时进行。
卸钩IO
该流程在内核模块退出时进行。
运行部署
该方法实现在Linux内核模块中,为了防止其它内核模块可能也注册了原始套接字接口,因此需要在操作系统启动时优先加载。
posted @
2016-07-14 10:27 春秋十二月 阅读(2444) |
评论 (3) |
编辑 收藏
描述
TCP连接跟踪是网络流控和防火墙中的一项重要的基础技术,当运用于主机时,连接必与进程相关联,要么是主动发出的,要么是被动接受的,当后代进程被动态创建时,由于文件描述符的继承,一个连接就会被这个进程树中的所有进程共享;当一个进程发出或接受多个连接时,就拥有了多个连接。本方法可用于网络安全产品中,监控TCP连接及所属进程,能准确并动态地知道一个连接被哪些进程共享,一个进程拥有哪些连接。
特点
操作系统自带的netstat工具只是关联到了一个根进程,无法看到拥有该连接的所有进程,查看进程拥有的全部连接也不方便。该方法的特点是实时跟踪、查看连接与进程相关信息方便、支持连接的管控。
实现
本方法通过内核安全的十字链表实现了连接与进程的相关性,连接信息结构体含有一个所属进程链表头,进程信息结构体含有一个拥有连接链表头,通过十字链表结点链接,x方向链接到进程的连接链表,y方向链接到连接的进程链表,如下图所示
进程1为根进程,进程2,...,进程n为进程1的后代进程;连接1,连接2,...,连接n为进程1产生的连接。node(x,y)为十字链表结点,用于关联连接与进程,x对应进程编号,y对应连接编号,每个node包含了所属的连接和进程指针,每行和每列都是一个双向循环链表(循环未画出),每个链表用一个自旋锁同步操作。
动态跟踪的过程包括4个方面:进程创建、进程退出、连接产生、连接销毁。在Linux下,可通过拦截内核函数do_fork挂钩进程创建,拦截do_exit挂钩进程退出;可通过拦截inet_stream_ops的成员函数connect和accept挂钩连接产生,拦截成员函数release挂钩连接销毁。下面为4个方面对应的流程图,由于所有外层加锁前已禁止本地中断和内核抢占,因此内层加锁前就不必再禁止本地中断和内核抢占了。
进程创建
将copy_node插入到c的进程链表末尾,即为y方向增加(下同);插入到p的连接链表末尾,为x方向增加(下同)。
进程退出
从c的进程链表中移除node,即为y方向移除(下同);再从p的连接链表中移除node,即为x方向移除(下同)。
连接产生
当进程发出连接或接受连接时,调用此流程。
连接销毁
当某个进程销毁连接时,调用此流程。
posted @
2016-07-13 11:24 春秋十二月 阅读(1509) |
评论 (0) |
编辑 收藏
描述
在P2P应用系统中,当需要与处于不同私网的对方可靠通信时,先尝试TCP打洞穿透,若穿透失败,再通过处于公网上的代理服务器(下文简称proxy)转发与对方通信,如下图所示
终端A先连接并发消息msg1到proxy(连接为c1);proxy再发消息msg2给P2P服务器,P2P服务器收到消息后通过已有的连接发消息msg3给终端B,B收到msg3后,连接并发消息msg4到proxy(连接为c2)。这4个消息格式由应用层协议决定,但必须遵守的规范如下:
◆ msg1至少包含B的设备ID。
◆ msg2至少包含B的设备ID和c1的连接ID。
◆ msg3至少包含c1的连接ID和连接代理指示。
◆ msg4至少包含B的设备ID和c1的连接ID。
proxy为多线程架构,当接受到若干连接时,如果数据相互转发的两个连接(比如上图中的c1和c2)不在同一线程,由于一个连接写数据到另一连接的发送队列,而另一连接从发送队列读数据以发送,那么就要对另一连接的发送队列(或缓冲区)加锁,这样一来由于频繁的网络IO而频繁地加解锁,降低了转发效率,因此为了解决这一问题,就需要调度TCP连接到同一线程。
特点
本方法能将数据转发的两边连接放在同一线程,从而避免了数据转发时的加锁,由于是一对一的连接匹配,因此也做到了每个线程中连接数的均衡。
实现
工作原理
proxy的主线程负责绑定知名端口并监听连接,工作线程负责数据转发。当接受到一个连接时,按轮转法调度它到某个工作线程,在那个线程内接收分析应用层协议数据,以识别连接类型,即判断连接是来自数据请求方还是数据响应方,为统一描述,这里把前者特称为客户连接,后者为服务连接,连接所在的线程为宿主线程。如果是客户连接,那么先通知P2P服务器请求对应的客户端来连接代理,并等待匹配;如果是服务连接,那么就去匹配客户连接,在匹配过程中,如果服务连接和客户连接不在同一线程内,那么就会调度到客户连接的宿主线程,匹配成功后,就开始转发数据。
为了加快查找,分2个hash表存放连接,客户连接放在客户连接hash表中,以连接ID为键值;服务连接放在服务连接hash表中,以设备ID为键值。
TCP连接调度
包括新连接的轮转、识别连接类型、匹配客户连接1、匹配客户连接2和关闭连接共5个流程,如下一一所述。
新连接的轮转
该流程工作在主线程,如下图所示
索引i初始为0,对于新来的连接,由于还不明确连接类型,所以先放入客户连接表中。
识别连接类型
该流程工作在工作线程,当接受到一个连接时开始执行,如下图所示
当连接类型为服务连接时,从客户连接表转移到服务连接表。
匹配客户连接1
该流程工作在工作线程,当接受到一个服务连接时开始执行,如下图所示
匹配客户连接2
该流程工作在工作线程,当服务连接移到客户连接的宿主线程时开始执行,如下图所示
这里的流程和匹配客户连接流程1有些类似,看起来好像做了重复的判断操作,但这是必要的,因为在服务连接转移到另一线程这个瞬间内,客户连接有可能断开了,也有可能断开后又来了一个相同连接ID的其它客户连接,所以要重新去客户连接表查找一次,然后进行4个分支判断。
关闭连接
该流程工作在工作线程内,当连接断开或空闲时执行。当一边读数据出错时,不能马上关闭另一边连接,得在另一边缓冲区数据发送完后才能关闭;当一边连接写数据出错时,可以马上关闭另一边连接,如下图所示
posted @
2016-07-12 16:59 春秋十二月 阅读(1775) |
评论 (0) |
编辑 收藏
摘要: 为了使nginx支持windows服务,本文阐述以下主要的改进实现。ngx_main函数 为了在SCM服务中复用main函数的逻辑,将其重命名为ngx_main,并添加第3个参数is_scm以兼容控制台运行方式,声明在core/nginx.h中。
Code highligh...
阅读全文
posted @
2016-07-12 15:31 春秋十二月 阅读(4477) |
评论 (0) |
编辑 收藏