概述
简单的说,我们都知道http/smtp/dns/ftp,这些协议都是基于网络模型中的应用层,那么socket是什么呢? socket本质上讲的基于应用层以下的传输层(TCP/UDP)中的一个请求。
例如我们想要进行一个聊天程序的设计,如果我们采用HTTP协议,我们都知道HTTP协议中,当客户端发送给服务端数据之后,就会进行关闭,不能持续性链接。因此我们在设计聊天程序的时候,不可能一直监听是否有HTTP请求,这样是非常麻烦的。
所以这个时候我们就需要使用socket进行编写。
我们使用Python中的socket库。
socket库的使用
首先我们需要先创建一个socket对象。
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
其中第一个参数代表,使用ipv4,其二是指使用TCP协议。
然后我们通过socket对象下面的bind函数绑定一个地址与端口。
s.bind(('0.0.0.0', 8000))
s.listen(5)
这里需要注意,如果我们写成127.0.0.1,它就只基于当前本机的一个地址,局域网是无法访问到的,所以我们写成0.0.0.0。
之后使用listen函数设置连接,其中5代表,最大连接数为5。当然,这个根据你的情况进行编写。
看到这里,你应该已经猜到了,我们接下来是要编写一个服务端的程序。那么我们接下来就应该是处理客户端发送的请求。
自然,我们需要先获得服务端的连接。
c, addr = s.accept()
我们使用accept函数获取连接请求,它会返回两个参数,其一是用来响应目标的对象,其二是目标的地址。当服务端发送请求的时候,我们就可以使用第一个参数进行获得请求,并且使用这个对象进行响应。
data = c.recv(1024)
print(data.decode('utf8'))
reDate = raw_input()
c.send(reDate.encode('utf8'))
通过第一个参数c下的recv函数进行接收数据,其中参数的意思是一次接收多少字节的数据。
然后通过send函数进行响应数据。
值得注意的是我们需要使用utf8的方式对其解码与编码
以上便是我们服务端的代码。客户端实际上大同小异,无非是连接到客户端,然后对其发送、响应。
Client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Client.connect(('127.0.0.1', 8000))
while True:
reData = raw_input()
Client.send(reData.encode("utf8"))
Data = Client.recv(1024)
print(Data.decode('utf8'))
使用connect函数进行连接服务端,这里就需要写成127.0.0.1了。之所以代码中写一个while是因为想要让它一直循环下去,做出类似于聊天程序的效果。
完整代码
server
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('0.0.0.0', 8000))
s.listen(5)
c, addr = s.accept()
while True:
data = c.recv(1024)
print(data.decode('utf8'))
reDate = raw_input()
c.send(reDate.encode('utf8'))
client
import socket
Client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Client.connect(('127.0.0.1', 8000))
while True:
reData = raw_input()
Client.send(reData.encode("utf8"))
Data = Client.recv(1024)
print(Data.decode('utf8'))
聊天程序
实际上当学习玩前面的知识后,几乎我们已经有能力制作一个简易的聊天工具了~ 不过如果你想制作一个多对多用户聊天的,那你就有必要了解一下线程相关的知识。
视频演示
server
# coding=utf-8
import socket
import threading
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
user = {}
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
port = 8000
maxUser = 5
s.bind(('0.0.0.0', port))
print 'server start success!'
print '监听{}端口.最多承载{}人。'.format(port, maxUser)
print '————————————————————————'
s.listen(maxUser)
def sent(name, content, c):
print("向{}发送信息:{}".format(name, content))
if not user.has_key(name):
c.send('对方不在线!'.encode('utf8'))
else:
user[name].send(content.encode('utf8'))
def init(name, c):
user[name] = c
def handleSock(c, addr):
while True:
data = c.recv(1024).decode('utf8')
target = data[data.index("[") + 1:data.index("]")]
content = data[data.index("]") + 1:len(data)]
if target == 'login':
init(name=content, c=c)
c.send('{} login succss'.format(content).encode('utf8'))
else:
name = data[data.index("{") + 1:data.index("}")]
content = name + ':' + content
sent(name=target, content=content, c=c)
if __name__ == '__main__':
while True:
c, addr = s.accept()
clientThread = threading.Thread(target=handleSock, args=(c, addr))
clientThread.start()
client
# coding=utf-8
import socket
import threading
import time
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
Client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
re = Client.connect_ex(('127.0.0.1', 8000))
while re != 0:
print '服务器不在线。'
time.sleep(1)
re = Client.connect_ex(('127.0.0.1', 8000))
name = raw_input('输入你的名称:')
Client.send('[login]{}'.format(name).encode('utf8'))
def getMsg():
while True:
Data = Client.recv(1024)
print(Data.decode('utf8'))
if __name__ == '__main__':
getMsgThread = threading.Thread(target=getMsg)
getMsgThread.start()
time.sleep(1)
tarGetName = raw_input('好友名称:')
tarGetName = '{' + name + '}' + '[' + tarGetName.strip() + ']'
print '×-----------------------------------------×'
print '| 「KuM」 blog: ku-m.cn |'
print '×-----------------------------------------×'
while True:
order = raw_input()
if order == 'quit':
break
reData = tarGetName + order
Client.send(reData.encode("utf8"))
模拟HTTP请求
以上我们简单的使用了Python中的socket库。我们知道socket与http的区别似乎就是一个是响应后就断掉,一个是可以进行持久性链接。那么我们通过socket库是否可以发起http请求呢?答案自然是可以的。
首先我们拿到一个域名需要先对它解析,解析成域名(host)与路径(path),因为我们后面需要按照http协议格式的数据包会用到。我们可以引入urlparse包,当然也可以自己进行修改。
def getHtml(url):
url = urlparse.urlparse(url)
host = url.netloc
path = url.path
if path == "":
path = "/"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, 80))
s.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format(path, host).encode('utf8'))
Data = b''
while True:
d = s.recv(1024)
if d:
Data += d
else:
break
Data = Data.decode('utf8')
return Data
值得注意的是,我们的recv函数每次都只获取1024个字节,所以我们需要循环进行获得,然后才能拿到完整的数据。而send函数中写入的便是完整的一个http协议的格式,这里就不再赘述,请自行百度。