1. 源代码
#!/usr/bin/env python
# coding=utf8
# si.hairui : @2016.11.24
# Http Client: @Python 2.7.5
# Platform : @windows 7
# ---------------------------------------------
# 0.先设置机器注册表中TIME_WAIT的值,再运行本程序:
# reg /?
# reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\Parameters" /v "TcpTimedWaitDelay" /t REG_DWORD /d 2 /f
# reg delete "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\Parameters" /v "TcpTimedWaitDelay" /f
# 1.上电之后读取配置文件,获取客户端列表和服务器列表;
# 2.校验配置文件中客户端IP的真实性(有效性);
# 3.解析服务端URL字段内容
# 4.注册线程,开始并发请求
# ---------------------------------------------
# ---------------------------------------------
# 模块导入
# ---------------------------------------------
import socket
import httplib
import threading
from time import sleep, ctime
# ---------------------------------------------
# 配置开关
# ---------------------------------------------
PRINT_SW = 0 # 业务打印开关,0-关,1-开
PRINT_THR_SW = 0 # 线程打印开关,0-关,1-开
WHILE_TRUE_SW = 1 # 死循环开关,0-关,1-开
# ---------------------------------------------
# 全局变量
# ---------------------------------------------
#TCP_REQUEST = 0 # TCP请求总数,线程需要互斥锁访问
#TCP_REQUEST_LOCK = threading.Lock() # 互斥锁访问
#TCP_ESTABLISHED = 0 # TCP建链成功计数器,线程需要互斥锁访问
#TCP_ESTABLISHED_LOCK = threading.Lock() # 互斥锁访问
# 客户端信息列表
HTTP_CLIENT_LIST = []
'''
HTTP_CLIENT_LIST = [
{'ip': '192.168.0.218', 'portbegin': 464, 'portend': 464},
{'ip': '192.168.0.220', 'portbegin': 34684, 'portend': 34685}
]
'''
# 服务器信息
HTTP_SERVER_LIST = []
'''
HTTP_SERVER_LIST = [
'http://192.168.0.236/web/page/',
'http://192.168.0.235:80/web/video/aboutHFS/HFS.txt'
]
'''
# ---------------------------------------------
# 函数实现
# ---------------------------------------------
# 修改默认buff(详见socket.py Line:238, default_bufsize = 8192)
def buffInit():
socket._fileobject.default_bufsize = 8192 * 10
socket._fileobject._wbuf_len = 8192 * 10
# 读取配置文件,返回配置文件字符串
def readCfgFile():
try:
with open('config.cfg', 'r') as f:
return f.read() # 返回字符串
except:
print "Error: ReadCfgFile fail!"
return None
# 获取本地业务地址,放入序列clientIps
def getServiceIp(clientIps):
hostname = socket.gethostname()
localIP = socket.gethostbyname(hostname)
if PRINT_SW:
print "Hostname:\n %s" % hostname
print "Local IP:\n %s" % localIP
ipList = socket.gethostbyname_ex(hostname)
if PRINT_SW: print "ipList", ipList, "\n" # ('Harry-PC', [], ['192.168.1.103', '192.168.1.250', '192.168.1.251', ...)
for ips in ipList:
# 过滤空序列、主机名
if ips and (not isinstance(ips, str)): # ips是一个序列,其中每个元素是IP地址字符串
if PRINT_SW: print "External IP:"
for ip in ips:
if PRINT_SW: print " %s" % ip
clientIps.append(ip)
# 获取客户端列表,eg: HTTP_CLIENT_LIST = list(getClientInfo())
def getClientInfo():
cfginfo = eval(readCfgFile())
# 校验配置文件中的IP
# a.首先获取本地业务IP地址
locIps = []
getServiceIp(locIps)
# b.然后对比配置文件中的地址
validDicts = [] # 保存配置文件中的合法元素
for sockdict in cfginfo['clients']:
try:
locIps.index(sockdict['ip'])
except ValueError: # 如果发生异常,说明sockdict中的IP地址不在本PC上,则丢掉
print "Error: " + sockdict['ip'] + " in <config.cfg> is not valid, it will be discarded!!!"
continue
else: # 没有异常,则保存该字典
validDicts.append(sockdict)
if PRINT_SW: print validDicts
return validDicts
# 获取服务端列表,eg: HTTP_SERVER_LIST = list(getServerInfo())
def getServerInfo():
cfginfo = eval(readCfgFile())
return cfginfo['servers']
# 根据URL获取Server的IP、port、filepath,以字典的形式返回
def getServerSocket(url):
strIp = ""
port = 0
filepath = ""
if 0 != url.find("http://"): # URL必须以“http://”开头(暂时不考虑https的场景)
print "Error: URL is invalid: %s !!!" % url
return None
# 首先,去掉“http://”
urlUnhttp = url[7:]
# 然后判断有无“/”
firstXieGangLoc = urlUnhttp.find("/") # urlUnhttp中第一个“/”下标
firstMaoHaoLoc = urlUnhttp.find(":") # urlUnhttp中第一个“:”下标
if (-1) == firstXieGangLoc and (-1) == firstMaoHaoLoc: # "192.168.0.235"
strIp = urlUnhttp
port = 80
filepath = "/"
elif (-1) == firstXieGangLoc and firstMaoHaoLoc > 0: # "192.168.0.235:8988"
strIp = urlUnhttp[: firstMaoHaoLoc]
port = int(urlUnhttp[firstMaoHaoLoc + 1:])
filepath = "/"
elif firstXieGangLoc > 0 and (-1) == firstMaoHaoLoc: # "192.168.0.235/..."
strIp = urlUnhttp[: firstXieGangLoc]
port = 80
filepath = urlUnhttp[firstXieGangLoc:]
elif firstXieGangLoc > 0 and firstMaoHaoLoc > 0: # "192.168.0.235:8988/..."
strIp = urlUnhttp[: firstMaoHaoLoc] # 这种情况下,默认URL中“:”在“/”左边
port = int(urlUnhttp[firstMaoHaoLoc + 1: firstXieGangLoc])
filepath = urlUnhttp[firstXieGangLoc:]
else:
print "Error: URL is unable reslute: %s!!!" % url
svrdict = {}.fromkeys(["ip", "port", "filepath"])
svrdict["ip"] = strIp
svrdict["port"] = port
svrdict["filepath"] = filepath
return svrdict
# 串行轮询服务器和客户端发起HTTP请求,eg: runHttpCliet(HTTP_CLIENT_LIST, HTTP_SERVER_LIST)
'''
def runHttpCliet(clients=[], servers=[]):
if (None == clients) or (None == servers): # 保护一下
print "Error: clients or servers is None!!!"
return
for urlstr in servers:
svrdict = getServerSocket(urlstr) # 解析URL
if None == svrdict or None == svrdict["ip"] or None == svrdict["port"]:
print "Error: Server is invalid!!!", svrdict
continue # 执行下一个server
if PRINT_SW:
print "svrdict =", svrdict
for httpClient in clients:
if PRINT_SW:
print "httpClient =", httpClient
for clientPort in range(httpClient['portbegin'], httpClient['portend'] + 1):
try:
myHttpConn = httplib.HTTPConnection(svrdict["ip"], svrdict["port"],
timeout = 2, # 设置连接超时等待(单位:s)
source_address = (httpClient["ip"], clientPort))
myHttpConn.debuglevel = 0 # 调试模式开关
if 0 == myHttpConn.debuglevel:
print " ---- Http Request ----"
print " " + httpClient["ip"] + ":", clientPort, \
" --> " + svrdict["ip"], svrdict["port"], ": " + svrdict['filepath']
myHttpConn.request("GET", svrdict['filepath']) # 资源路径以“/”开始
myHttpResp = myHttpConn.getresponse()
if 0 == myHttpConn.debuglevel:
print " ", myHttpResp.status, myHttpResp.reason # 响应码和状态信息
# 此处必须读响应内容,如果不读取,buff将溢出!!!
data1 = myHttpResp.read()
pass
except:
print "!!!---------- Exception ------------------------"
continue
finally:
# print data1 # 打印响应内容
print " Http(%s:%s --> %s:%s) will be closed!" % \
(httpClient["ip"], str(clientPort), svrdict["ip"], str(svrdict["port"]))
myHttpConn.close() # 关闭连接
'''
# 并发轮训服务器发起HTTP请求,eg: runHttpCliet(clientsDic, HTTP_SERVER_LIST, delaySeconds)
# delaySeconds: 0-5,从程序开始运行时到发起链接的时延,单位:秒
def runHttpCliet(clientsDic={}, servers=[], delaySeconds=0, whileTrue=0):
'''
关于函数中sleep(m)的解释:
在TCP/IP终止连接的四次握手中,当最后的ACK回复发出后,有个2MSL的时间等待,MSL指一个片段在网络中最大的存活时间,
这个时间一般是30秒,所以基本上过60秒后就可以重新连接,详见:
http://www.bkjia.com/Pythonjc/868893.html
http://www.dewen.net.cn/q/8606
http://www.51testing.com/html/48/202848-249774.html
http://blog.youkuaiyun.com/mhfh611/article/details/8769617/
对于 Windows,修改注册表(win+R运行输入regedit),在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
上添加一个DWORD类型的值TcpTimedWaitDelay,一般认为不要少于60,不然可能会有麻烦。
进入Windows命令行窗口:
reg /?
reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\Parameters" /v "TcpTimedWaitDelay" /t REG_DWORD /d 30 /f
reg delete "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\Parameters" /v "TcpTimedWaitDelay" /f
'''
if (None == clientsDic) or (None == servers): # 保护一下
print "Error: clients or servers is None!!!"
return
for urlstr in servers:
svrdict = getServerSocket(urlstr) # 解析URL
if None == svrdict or None == svrdict["ip"] or None == svrdict["port"]:
print "Error: Server is invalid!!!", svrdict
continue # 执行下一个server
if PRINT_SW: print "svrdict =", svrdict
sleep(delaySeconds) # tcp建链时间间隔
whileTrueSw = 1
while whileTrueSw: # 开始死循环
for clientPort in range(clientsDic['portbegin'], clientsDic['portend'] + 1):
try:
# TCP建链统计
#global TCP_REQUEST, TCP_REQUEST_LOCK # 多线程是共享资源的,使用全局变量
#if TCP_REQUEST_LOCK.acquire():
#TCP_REQUEST += 1
#print " TCP_REQUEST=", TCP_REQUEST
#TCP_REQUEST_LOCK.release()
myHttpConn = httplib.HTTPConnection(svrdict["ip"], svrdict["port"],
# timeout = 3, # 设置连接超时等待(单位:s)
source_address=(clientsDic["ip"], clientPort))
myHttpConn.debuglevel = 0 # 调试模式开关
if 0 == myHttpConn.debuglevel:
print " ---- Http Request ----"
print " " + clientsDic["ip"] + ":", clientPort, \
" --> " + svrdict["ip"], svrdict["port"], ": " + svrdict['filepath']
try:
myHttpConn.request("GET", svrdict['filepath']) # 资源路径以“/”开始
except:
print " !!!---------- Exception: myHttpConn.request ----------------"
continue
# TCP成功建链统计
#global TCP_ESTABLISHED, TCP_ESTABLISHED_LOCK # 多线程是共享资源的,使用全局变量
#if TCP_ESTABLISHED_LOCK.acquire():
#TCP_ESTABLISHED += 1
#print " TCP_ESTABLISHED=", TCP_ESTABLISHED
#TCP_ESTABLISHED_LOCK.release()
try:
myHttpResp = myHttpConn.getresponse()
except:
print " !!!---------- Exception: myHttpConn.getresponse ----------------"
continue
if 0 == myHttpConn.debuglevel:
print " ", myHttpResp.status, myHttpResp.reason # 响应码和状态信息
# 此处必须读响应内容,如果不读取,buff将溢出!!!
data1 = myHttpResp.read()
pass
except (), e:
print " !!!---------- Exception: %s ------------------------" % e
continue
finally:
# print data1 # 打印响应内容
print " Http(%s:%s --> %s:%s) will be closed!" % \
(clientsDic["ip"], str(clientPort), svrdict["ip"], str(svrdict["port"]))
myHttpConn.close() # 关闭连接
sleep(20)
if 0 == WHILE_TRUE_SW:
whileTrueSw -= 1
else:
pass
# 客户端并发线程子类,父类:threading.Thread
# 每一个客户端IP独占一个线程,线程对客户端列表和服务端列表数据只读不写
class clientThread(threading.Thread):
def __init__(self, func, args, name='', printThrSw=0):
threading.Thread.__init__(self)
self.func = func
self.args = args
self.name = name
self.printThrSw = printThrSw
def getResult(self):
return self.res
def run(self):
if self.printThrSw:
print 'starting', self.name, 'at:', ctime(), ". delaySeconds=", self.args[2]
self.res = apply(self.func, self.args)
if self.printThrSw:
print self.name, 'finished at:', ctime()
# 客户端注册线程
def clientRegThr():
HTTP_CLIENT_LIST = list(getClientInfo()) # 构造客户端
if PRINT_SW: print "HTTP_CLIENT_LIST =", HTTP_CLIENT_LIST
HTTP_SERVER_LIST = list(getServerInfo()) # 构造服务端
if PRINT_SW: print "HTTP_SERVER_LIST =", HTTP_SERVER_LIST
threadNum = len(HTTP_CLIENT_LIST) # 计算线程个数,也就是客户端的个数
if PRINT_SW: print "threadNum =", threadNum
clientThreads = [] # 线程列表
for i in range(threadNum): # 线程编号从0开始
thr = clientThread(
func=runHttpCliet,
# args = (HTTP_CLIENT_LIST[i], HTTP_SERVER_LIST, random.randint(1,5)), # random.randint(1,5) == [1,5]
args=(HTTP_CLIENT_LIST[i], HTTP_SERVER_LIST, i, WHILE_TRUE_SW), # 打散并发时间
name="clientThread_" + str(HTTP_CLIENT_LIST[i]['ip']),
printThrSw=PRINT_THR_SW
)
clientThreads.append(thr) # 添加线程至列表
for i in range(threadNum):
clientThreads[i].start()
for i in range(threadNum):
clientThreads[i].join()
# App的main()入口
def main():
buffInit()
clientRegThr()
# ---------------------------------------------
# main模块
# ---------------------------------------------
if __name__ == "__main__":
main()
2.配置文件
# ----------------------------------------------------------------------------
# config.cfg
# clients: {"ip": "客户端的IP地址", "portbegin": 起始端口号, "portend": 结尾端口号}
# servers: 暂时只支持 “http://IP地址”的格式的URL
# 同一个IP地址不允许在下面出现2次或多次
# ----------------------------------------------------------------------------
{
"clients" : [
{ "ip": "192.168.1.254", "portbegin": 2343, "portend": 12343 },
{ "ip": "192.168.1.253", "portbegin": 16357, "portend": 26357 },
{ "ip": "192.168.1.252", "portbegin": 27134, "portend": 37134 },
{ "ip": "192.168.1.251", "portbegin": 38900, "portend": 50000 }
],
"servers" : [
#"http://192.168.1.103/KMP/yfwy.mp3",
#"http://192.168.1.103/KMP/16.jpg"
"http://www.cplusplus.com",
"http://www.appinn.com/",
"http://www.downxia.com/"
]
}
3.客户端运行时
4.服务器运行时