网络编程基础(一)
一、套接字
1.分类
1.1面向连接的套接字
面向连接的套接字,需要在通信之前就要先建立一个连接,也称为虚拟电路或者流套接字。
实现这种连接类型的主要协议为`传输控制协议TCP`,创建`TCP`套接字必须要使用`SOCK_STREAM`作为套接字类型。
1.2面向无连接的套接字
在通信开始之前不需要进行建立连接,也被称为数据报。
这种连接类型的主要协议为`用户数据报服务UDP`, 创建这种套接字,必须要使用`SOCK_DGRAM`作为套接字类型.
二、基本使用
1.socket模块函数
创建socket的一般语法:
socket(socket_family, socket_type, protocol=0)
eg: TCP
tcp_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
eg: UDP
usp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
如果我们使用from socket import *
导入socket属性,即将socket属性引入命名空间中,那么就可以使用下面的简短代码创建套接字:
tcp_sock = socket(AF_INET, SOCK_STREAM)
2.常见的套接字对象方法:
名称 | 描述 |
---|---|
服务器套接字方法 | |
s.bind() | 将地址(主机名,端口号对)绑定到套接字上 |
s.listen() | 设置并启动TCP监听器 |
s.accept() | 被动接受TCP客户端的连接,一直等待知道连接到达(阻塞) |
客户端套接字方法 | |
s.connect() | 主动发起TCP服务器连接 |
s.connect_ex() | connect()的扩展版本,此时会以错误码形式返回问题,而不是抛出异常普通的套接字方法 |
s.recv() | 接收TCP消息 |
s.recv_into() | 接收TCP消息到指定的缓冲区 |
s.send | 发送TCP消息 |
s.sendall() | 完整的发送TCP消息 |
s.recvfrom() | 接收UDP消息 |
s.recvfrom_into() | 接收UDP消息到指定的缓冲区 |
s.sendto() | 发送UDP消息 |
s.getpeername() | 连接到套接字(TCP)的远程地址 |
s.getsockname() | 当前套接字的地址 |
s.getsockopt() | 返回给定套接字选项的值 |
s.setsockopt() | 设置给定套接字选项的值 |
s.shutdown() | 关闭连接 |
s.close() | 关闭套接字 |
s.detach() | 在未关闭文件描述符的情况下关闭套接字,返回文件描述符 |
s.ioctl() | 控制套接字的模式(仅仅支持windows系统) |
s.functl() | 控制套接字的模式(对于POSIX系统) |
面向阻塞的套接字方法 | |
s.setblocking() | 设置套接字的阻塞或者非阻塞模式 |
s.settimeout() | 设置阻塞套接字的操作的超时时间 |
s.gettimeout() | 获取阻塞套接字操作的超时时间 |
面向文件的套接字方法 | |
s.fileno() | 套接字的文件描述符 |
s.makefile() | 创建于套接字关联的文件对象 |
数据属性 | |
s.family() | 套接字家族 |
s.tyep() | 套接字类型 |
s.proto() | 套接字协议 |
三、创建服务器和客户端
(一).TCP的Server和Client
1.TCP server
伪代码模版块:
ss = socket() # 创建服务器套接字
ss.bind() # 套接字与地址绑定
ss.listen() # 监听连接
inf_loop: # 服务器无限循环
cs = ss.accept() # 接收客户端连接
comm_loop(): # 通信循环
cs.recv()/cs.send() # 对话(接收/发送)
cs.close() # 关闭客户端套接字
ss.close() # 关闭服务器套接字 (可选)
简述:
1、所有的套接字通过使用
socket.socket()
函数来创建的。
2、由于服务器需要占用一个端口并等待客户端的请求,所以其必须绑定 到一个本地地址,TCP是面向连接的,在其开始操作之前,必须安装一些基本设施。
3、监听连接后,服务器就可以开始他的无线循环
4、调用accept()
之后,开启了(单线程)服务器,等待连接。默认下,accept()是阻塞的。
5、服务器接收连接之后,就会返回(利用accept()
)一个独立的客户端套接字,用来进行数据交换。
客户端关闭连接之后,不会影响服务端的无线循环。但可以使用close()
函数主动退出。
Demo:
# 一个TCP-server,
# 接收客户端的信息,并打上时间戳,返回给client
# 返回格式: time + data
#
# coding=utf-8
from socket import *
from time import ctime
# 相关属性
host = '' # 变量是空白的,对bind()函数的标识,其可以使用任何可用地址。
port = 21567
buffer_size = 1024
address = (host, port)
# 建立连接
tcp_server = socket(AF_INET, SOCK_STREAM)
tcp_server.bind(address)
tcp_server.listen(5) # 同时连接的最大值
# 逻辑
while True:
print ("[+] waiting for connection...")
tcp_client, address = tcp_server.accept() # 得到连接客户端的套接字
print ("[+] ...connected from:", address)
while True:
data = tcp_client.recv(buffer_size) # 得到客户端发送的数据
if not data:
break
tcp_client.send('[M] [%s] %s' % (ctime(), data)) # 返回已处理数据
tcp_client.close()
tcp_server.close() # 这一行永远不会执行
2.TCP Client
# 伪代码板块
cs = socket() # 创建客户端套接字
cs.connect() # 尝试连接服务器
comm_loop: # 通信循环
cs.send()/cs.recv() # 对话(发送/接收)
cs.close() # 关闭客户端套接字
简述:
1、利用socket.socket()创建的
2、利用Connect连接到服务器
Demo:
# coding=utf-8
from socket import *
# 属性
host = 'localhost'
port = 21567
buffer_size = 1024
address = (host, port)
# 连接
tcp_client = socket(AF_INET, SOCK_STREAM)
tcp_client.connect(address)
# 逻辑处理
while True:
data = raw_input('>> ')
if not data:
break
tcp_client.send(data)
data = tcp_client.recv(buffer_size)
if not data:
break
print data
tcp_client.close()
(二).UDP的Server和Client
1. UDP server
# 伪代码板块
ss.socket() # 创建服务器套接字
ss.bind() # 绑定服务器套接字
inf_loop: # 服务器无限循环
cs = ss.recvfrom()/ss.sendto() # 接收/发送
ss.close() # 关闭服务器套接字
简述:
1、对于UDP的连接,不需要很多设置,在这里,它与TCP服务器最显著的差别: 没有为了确认成功通信而使客户端连接到一个独立的套接字
Demo:
# coding=utf-8
from socket import *
from time import ctime
# 属性
host = '' # 变量是空白的,对bind()函数的标识,其可以使用任何可用地址。
port = 21567
buffer_size = 1024
address = (host, port)
udp_server = socket(AF_INET, SOCK_DGRAM)
udp_server.bind(address)
while True:
print ("[+] waiting for connection...")
data, address = udp_server.recvfrom(buffer_size)
udp_server.sendto("[M] [%S] %s " % (ctime(), data), address)
print "[+] ...received from and returned to:", address
udp_server.close()
2.udp client
# 伪代码
cs = socket() # 创建客户端套接字
comm_loop: # 通信循环
cs.sendto()/cs.recvfrom # 对话(发送/接收)
cs.close() # 关闭客户端套接字
简述:
对于UDP连接,一旦创建了套接字对象,就进入对话循环中通信结束时,就会关闭套接字
Demo:
# coding=utf-8
from socket import *
# 属性
host = 'localhost'
port = 21567
buffer_size = 1024
address = (host, port)
# 连接
udp_client = socket(AF_INET, SOCK_DGRAM)
while True:
data = raw_input(">> ")
if not data:
break
udp_client.sendto(data, address)
data, address = udp_client.recvfrom(buffer_size)
if not data:
break
print data
udp_client.close()
四、有关socket模块的属性介绍
在下面给出一些socket常见的属性:
属性名称 | 描述 |
---|---|
数据属性 | |
AF_UNIX、AF_INET、AF_INET6、AF_NETLINK、AF_TIPC | python中所支持的套接字地址家族 |
SO_STREAM、SO_DGRAM | 套接字类型(TCP-流、UDP-数据报) |
has_ipv6 | 指示是否支持IPV6的布尔标记 |
异常 | |
error | 套接字的相关错误 |
herror | 主机和地址相关错误 |
gaierror | 地址相关错误 |
timeout | 超时时间 |
函数 | |
socket() | 以给定的地址家族、套接字类型和协议类型(可选)创建一个套接字对象 |
socketpair() | 以给定的地址家族、套接字类型和协议类型(可选)创建一对套接字对象 |
create_connection() | 常规函数,它接收一个地址(主机号、端口)对,返回套接字对象 |
fromfd() | 以一个打开的文件描述符创建一个套接字对象 |
ssl() | 通过套接字启动一个安全套接字层连接,不执行证书认证 |
getaddrinfo() | 获得一个五元组序列形式的地址信息 |
getnameinfo() | 给定一个套接字地址,返回(主机号、端口号)二元组 |
getfqdn() | 返回完整的域名 |
gethostname() | 返回当前主机名 |
gethostbyname() | 将一个主机名映射到它的IP地址 |
gethostbyname_ex() | gethostbyname()的扩展版本,它返回主机名、别名主机集合和IP地址列表 |
gethostbyaddr() | 将一个IP地址映射到DNS信息;返回与gethostbyname_ex()相同的三元组 |
getserverbyname()/getserverbyport() | 将一个服务名映射到一个端口,或者反过来;对于任何一个函数来说,协议名都是可选的。 |
ntohl()/ntohs() | 将来自网络的整数转换为主机字节顺序 |
htonl()/htons() | 将来自主机的整数转换为网络字节顺序 |
inet_aton()/inet_ntoa() | 将IP地址八进制字符串转换为32位的包格式,或者反过来;仅使用与IPV4地址 |
inet_pton()/inet_ntop() | 将IP地址字符串转换为打包的二进制格式,或者反过来。(同时适用IPV4、IPV6) |
getdefaulttimeout() | 以秒(浮点数)为单位返回默认套接字超时时间 |
setdefaulttimeout() | 以秒(浮点数)为单位设置默认套接字超时时间 |
以上就是对于网络编程基础的介绍,接下来,会以实现一个类QQ的通信进行练习。(见二)