在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。python中socket模块为操作系统的socket实现提供了一个python接口。

在Python中,import socket后,用socket.socket()方法来创建套接字,语法格式如下:
sk = socket.socket([family[, type[, proto]]])
参数说明:
♥family: 套接字家族,可以使AF_UNIX或者AF_INET。
♥type: 套接字类型,根据是面向连接的还是非连接分为SOCK_STREAM或SOCK_DGRAM,也就是TCP和UDP的区别。
♥protocol: 一般不填默认为0。
直接socket.socket(),则全部使用默认值。
关于s=socket.socket(family,type)还有一些实例方法
(1)s.bind((address,port))
将socket绑定到一个地址和端口上,通常用于socket服务端;
address必须是一个双元素元组,((host,port)),主机名或者IP地址+端口号。如果端口号正在被使用或者主机名或IP地址错误,则引发socket.error异常。端口号的使用是有限制的,在linux或者unix之中只有系统管理员才能使用1024以下的端口,这些端口号用于标准服务。
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(("172.16.0.18",30000))
(2)s.getsockname()
s.getsockname()返回一个客户机socket,带有客户机端的地址信息。accept方法返回一个双元素元组,形如(IP,port)。第一个元素是客户的IP地址,第二个元素是端口号。
(3)s.listen(backlog)
将socket设置成监听模式,可以监听backlog外来的连接请求,让服务器开始监听客户端发来的连接请求
这个方法设置服务器为监听状态,backlog制定了最多的连接数,至少为1。接到连接请求后,这些请求必须排队,如果队列达到backlog的数值,则拒绝接下来的连接请求。
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(("172.16.0.18",555))
s.listen(10)
(4)s.connect((address,port)) 与s.connect_ex((address,port))
将socket连接到定义的主机和端口上,通常用于socket客户端
import socket
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
state=sock.connect(("172.16.0.18",136))
二者的区别是:connect_ex 函数在遇到C层面的异常时不会抛出异常,而是返回状态码,0状态码表示正常,你也可以使用connect方法进行连接,但这样,就需要使用异常捕获机制来捕获ConnectionRefusedError 异常。
(5)s.recv(buflen[,flags])
从socket中接收数据,最多接收buflen个字符,一般填写1024个
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("172.16.0.18",555))
data=s.recv(1024)
(6)s.send(data[,flags])
通过socket发送指定的数据
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(("172.16.0.18",555))
s.listen(10)
connection,address=s.accept()
print( connectionsocket._socketobject object at 0x01DDDCE0>
>>> print address
('172.16.0.18', 21586)
s.send('hello,I am lybbn.cn')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
socket.error: [Errno 10057] 由于套接字没有连接并且(当使用一个 sendto 调用发送数
据报套接字时)
由上可见,s.send()发送数据的时候需要先建立socket连接,不然会出现error。使用recv方法和send方法发送和接受消息。发送和接收都是采用的字符串的形式。send方法返回已发送的字符个数。调用recv的时候,必须制定一个整数来控制本次调用所接受的最大的数据量。
recv方法接收数据时会进入阻塞状态,最后返回一个字符串,表示收到的数据。如果发送数据超过recv所允许,数据会被截断。多余的数据将缓冲于接收端。以后调用recv时,多余的数据会从缓冲区删除。
(7)s.close()
关闭socket连接,传输结束,通过调用close方法关闭连接。
TCP服务器端:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
ip_port = ("172.16.0.18", 30000)
try:
sk = socket.socket() # 创建套接字
sk.bind(ip_port) # 绑定服务地址、绑定端口
sk.listen(5) # 监听连接请求
print('启动socket服务端服务,等待客户端连接...')
conn, address = sk.accept() # 等待连接,此处自动阻塞
while True: # 一个死循环,直到客户端发送‘exit’的信号,才关闭连接
client_data = conn.recv(1024).decode() # 接收信息
if client_data == "exit": # 判断是否退出连接
exit("通信结束")
print("来自%s的客户端向你发来信息:%s" % (address, client_data))
conn.sendall('服务器已经收到你的信息'.encode()) # 回馈信息给客户端
conn.close() # 关闭连接
except Exception as msg:
print('socket,服务端发送失败{}!'.format(msg))
TCP客户端:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
ip_port = ("172.16.0.18", 30000)
s = socket.socket() # 创建套接字
s.connect(ip_port) # 连接服务器
while True: # 通过一个死循环不断接收用户输入,并发送给服务器
inp = input("请输入要发送的信息:").strip()
if not inp: # 防止输入空信息,导致异常退出
continue
s.sendall(inp.encode())
if inp == "exit": # 如果输入的是‘exit’,表示断开连接
print("结束通信!")
break
server_reply = s.recv(1024).decode()
print(server_reply)
s.close() # 关闭连接
缺陷就是,部署生产后,不持续连接,容易断开,需要重启服务端和客户端,才能发送消息,tcp模式,为了保持连接持久存活,需要在创建套接字之后进行一定的设置,首先打开TCP_KEEPALIVE选项,但是只打开这个选项是不够的,因为默认无数据收发2小时之后才开始发送心跳包,这时候连接基本上已经断开了。所以还需要设置通过心跳包保持连接存活的相关参数,例如无数据收发之后多久开始发送心跳包,以及多久发送一次心跳包。