connection, address = socket.accept()
调 用accept方法时,socket会时入“waiting”状态。客户请求连接时,方法建立连接并返回服务器。accept方法返回一个含有两个元素的 元组(connection,address)。第一个元素connection是新的socket对象,服务器必须通过它与客户通信;第二个元素 address是客户的Internet地址。
第五步是处理阶段,服务器和客户端通过send和recv方法通信(传输 数据)。服务器调用send,并采用字符串形式向客户发送信息。send方法返回已发送的字符个数。服务器使用recv方法从客户接收信息。调用recv 时,服务器必须指定一个整数,它对应于可通过本次方法调用来接收的最大数据量。recv方法在接收数据时会进入“blocked”状态,最后返回一个字符 串,用它表示收到的数据。如果发送的数据量超过了recv所允许的,数据会被截短。多余的数据将缓冲于接收端。以后调用recv时,多余的数据会从缓冲区 删除(以及自上次调用recv以来,客户可能发送的其它任何数据)。
传输结束,服务器调用socket的close方法关闭连接。
python编写client的步骤:
举例:
服务端:
#socket server端
#获取socket构造及常量
from socket import *
#''代表服务器为localhost
myHost = ''
#在一个非保留端口号上进行监听
myPort = 50007
#设置一个TCP socket对象
sockobj = socket(AF_INET, SOCK_STREAM)
#绑定它至端口号
sockobj.bind((myHost, myPort))
#监听,允许5个连结
sockobj.listen(5)
#直到进程结束时才结束循环
while True:
#等待下一个客户端连结
connection, address = sockobj.accept()
#连结是一个新的socket
print 'Server connected by', address
while True:
#读取客户端套接字的下一行
data = connection.recv(1024)
#如果没有数量的话,那么跳出循环
if not data: break
#发送一个回复至客户端
connection.send('Echo=>' + data)
#当socket关闭时eof
connection.close( )
客户端:
import sys
from socket import *
serverHost = 'localhost'
serverPort = 50007
#发送至服务端的默认文本
message = ['Hello network world']
#如果参数大于1的话,连结的服务端为第一个参数
if len(sys.argv) > 1:
serverHost = sys.argv[1]
#如果参数大于2的话,连结的文字为第二个参数
if len(sys.argv) > 2:
message = sys.argv[2:]
#建立一个tcp/ip套接字对象
sockobj = socket(AF_INET, SOCK_STREAM)
#连结至服务器及端口
sockobj.connect((serverHost, serverPort))
for line in message:
#经过套按字发送line至服务端
sockobj.send(line)
#从服务端接收到的数据,上限为1k
data = sockobj.recv(1024)
#确认他是引用的,是'x'
print 'Client received:', repr(data)
#关闭套接字
sockobj.close( )
参考:
/s/blog_523491650100hikg.html
创建一个socket客户端
#coding:utf-8#导入相关模块
importsocketimportsys#设置连接请求30S超时
socket.setdefaulttimeout(30)#IPV4协议、字节流(TCP协议)
try:
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)exceptsocket.error as e:print 'Socket Error:%s'%(str(e))
sys.exit()print 'Socket Created!'host= ''port= 80
#获取IP地址
try:
remote_ip= socket.gethostbyname(host) #此处同PHP中的gethostbyname()函数
exceptsocket.gaierror as e:print 'get %s ip error %s'%(host,str(e))
sys.exit()print 'Ip address of %s is %s'%(host,remote_ip)#连接服务器(SOCK_STREAM/TCP 套接字才有“连接”的概念,一些 Socket 如 UDP、ICMP 和 ARP 没有“连接”的概念)
try:
s.connect((remote_ip,port))exceptException as e:print 'Socket Connect to %s:%s Faile!!Error:%s'%(host,port,str(e))
sys.exit()print 'Socket Connect to %s on ip %s'%(host,remote_ip)#发送数据(根据HTTP/1.1协议)#请求消息的结构如下#请求方法 路径 HTTP版本\r\n ------>这为请求行#请求头域1: 请求头域的值1\r\n#请求头域2: 请求头域的值2\r\n#........................ ------->以上行为请求头#一个空行\r\n -------->头与主体间的空行#请求主体...... -------->请求主体#
##########请求行(方法)
msg = "GET / HTTP/1.1\r\n"
#请求header(HTTP/1.1请求必须包含主机头域,否则系统会以400状态码返回)
msg+= "Host: \r\n" #空行
msg+= "\r\n"
#请求body(由于我们这里是GET请求所以,body没有)
try:#这里也可用send()方法:不同在于sendall在返回前会尝试发送所有数据
#并且成功时返回是None,而send()则返回发送的字节数量。失败时都抛出异常。
s.sendall(msg)exceptException as e:print 'Send Failed!'sys.exit()print 'Msg Send Successfully!'
#获取服务器返回的数据
respone = s.recv(4096) #这里还可用recvfrom()、recv_into()等等
printrespone#释放资源
s.close()
输出:
Socket created
ip address of is 115.239.210.26
Msg Send successfully!
HTTP/1.1 200 OK
Date: Fri, 21 Feb 05:32:29 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: Keep-Alive
Vary: Accept-Encoding
Set-Cookie: BAIDUID=0D5536687056DF9F5C088AFBE27E42B7:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.
Set-Cookie: BDSVRTM=7; path=/
Set-Cookie: H_PS_PSSID=4850_1432_5186_5198_4261_5260_4760_5208; path=/; domain=.
P3P: CP=" OTI DSP COR IVA OUR IND COM "
Expires: Fri, 21 Feb 05:32:29 GMT
Cache-Control: private
Server: BWS/1.1
BDPAGETYPE: 1
BDQID: 0x95f7c521930a6c8e
BDUSERID: 0
为什么没有输出全部数据呢?看下面的函数:
socket.recv(bufsize[,flags])
Receive data from the socket. The return value is a string representing the data received. The maximum amount of data to be received at once is specified bybufsize. See the Unix manual pagerecv(2)for the meaning of the optional argumentflags; it defaults to zero.
Note
For best match with hardware and network realities, the value ofbufsizeshould be a relatively small power of 2,for example, 4096.
输出不完整。一些数据被漏掉。在TCP/IP协议下上面的情况会发生。在这个协议,数据以块进行传输。例如一个网页是500kb,但是最大快只有64kb。因此传输网页时,一次只传输了一部分而不是全部。
这就是问题产生的原因。recv函数能够等待直到收到所有的数据,但是对此,它必须知道整个要传输数据的大小。s.recv(4096 , socket.MSG_WAITALL) 会等待直到得到整个4096 bytes。现在如果实际响应的大小小于这个尺寸,函数会被阻塞一段时间,直到它返回。这不是我们所期待的。
解决:
解决方法是保持查询数据直到一个适当的超时发生,下面的例子中,我们就是这么做的:
def recv_timeout(the_socket,timeout=2):#make socket non blocking
the_socket.setblocking(0)#total data partwise in an array
total_data=[];
data='';#beginning time
begin=time.time()while 1:#if you got some data, then break after timeout
if total_data and time.time()-begin >timeout:break
#if you got no data at all, wait a little longer, twice the timeout
elif time.time()-begin > timeout*2:break
#recv something
try:
data= the_socket.recv(8192)ifdata:
total_data.append(data)#change the beginning time for measurement
begin=time.time()else:#sleep for sometime to indicate a gap
time.sleep(0.1)except:pass
#join all parts to make final string
return ''.join(total_data)
步骤是:
1.保证socket非阻塞。对此,socket不会等待,如果在recv调用没有数据。它会继续,如果没有数据可用。
2.按下面方式循环:保持调用recv,直到一个超时发生或recv完成。
现在这是一个简单的方法来证明recv函数在实际的应用中该如何工作。同样的函数能够进一步开发,然后变得更复杂,根据工作的协议,例如HTTP。
创建一个socket服务器端
#coding:utf-8
importsocketimportsysimporttimeimportQueueimportthreading
host= 'localhost'port= 8000
#创建socket对象
s =socket.socket(socket.AF_INET,socket.SOCK_STREAM)#绑定一个特定地址,端口
try:
s.bind((host,port))exceptException as e :print 'Bind Failed:%s'%(str(e))
sys.exit()print 'Socket bind complete!!'
#监听连接
s.listen(10) #最大连接数10
#创建连接队列
queue =Queue.Queue()#创建线程
classTaskThread(threading.Thread):def __init__(self):
threading.Thread.__init__(self)defrun(self):while 1:
t=queue.get()
t.send('welecome.....')#接收数据
client_data = t.recv(1024)
t.sendall(client_data)#释放资源
#t.close()
#接受连接
while 1:#将连接放入队列
conn,addr =s.accept()print 'Connected from %s:%s'%(addr[0],str(addr[1]))
queue.put(conn)#生成线程池
th =TaskThread()
th.setDaemon(True)
th.start()
queue.join()
s.close()