网络编程从大的方面说就是对信息的发送到接收,中间传输为物理线路的作用。
网络编程最主要的工作就是在发送端把信息通过规定好的协议进行组装包,在接收端按照规定好的协议把包进行解析,从而提取出对应的信息,达到通信的目的。中间最主要的就是数据包的组装,数据包的过滤,数据包的捕获,数据包的分析,当然最后再做一些处理,代码、开发工具、数据库、服务器架设和网页设计等。
套接字( Socket)是网络通信的关键,套接字也是应用程序和网络协议的接口。套接字由IP和端口号组成。
网络层通信主要由TCP和UDP两种通讯协议。数据传输的基本单元是数据包。网络层通讯的关键就是通过网络应用发送由网络层协议封装的数据包,从而使计算机通信。
套接字是为特定网络协议(例如TCP/IP,ICMP/IP,UDP/IP等)套件对上的网络应用程序提供者提供当前可移植标准的对象。它们允许程序接受并进行连接,如发送和接受数据。为了建立通信通道,网络通信的每个端点拥有一个套接字对象极为重要。
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
一般socket建立链接需要六个步骤,其中包括:socket.socket()创建socket对象、s.bind绑定地址到socket对象、s.listen监听地址端口、s.accept阻塞接受链接请求、s.send,s.recv方法处理通信数据、s.close关闭链接。
Python 提供了两个级别访问的网络服务:
低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统 Socket 接口的全部方法。
高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。
Python 中,我们用socket库的 socket()函数来创建套接字,语法格式如下:
socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
参数:
socket.socket(socket.AF_INET,socket.SOCK_STREAM)
使用给定的地址族,套接字类型和协议号来创建一个新套接字。
family
为指定的地址族:
socket.AF_UNIX
:只能够用于单一的Unix系统进程间通信
socket.AF_INET
:服务器之间的网络通信(ipv4协议的TCP和UDP)ipv4,默认为这个
socket.AF_INET6
:服务器之间的网络通信ipv6
type
为指定的套接字类型:
socket.SOCK_STREAM
:面向连接的TCP,默认为这个
socket.SOCK_DGRAM
:面向非连接的UDP
socket API
socket.scoket(…)可以创建一个scoket对象,socket对象只是一个TCP或UDP数据包的装载对象,主机通过该对象发送数据包。因此通讯的双方都需要socket对象。一方作为数据的接收端服务端,一方作为数据的发送端客户端。socket对象提供了API来创建服务端和客户端程序。
方法 | 描述 |
---|---|
服务器API | 服务器端套接字 |
socket.bind() | 绑定地址(host,port)到套接字, 在 AF_INET下,以元组(host,port)的形式表示地址。用于服务端,绑定监听的端口,可以用0.0.0.0接收所有ip,127.0.0.0只接受本机服务。 |
socket.listen() | 开始 TCP 监听。参数指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为 1,大部分应用程序设为 5 就可以了,是服务端程序。 |
socket.accept() | 被动接受TCP客户端连接,(阻塞式)等待连接的到来,没有来自客户端的请求是处于阻塞状态。accept方法会返回一个含有两个元素的元组,(fd,addr)fd, addr = self._accept() 。第一个元素是新的socket对象,服务器通过它与客户端通信。第二个元素是客户端的地址及端口信息。 |
客户端API | 客户端套接字 |
socket.connect() | 主动初始化TCP服务器连接,参数的格式为元组(hostname,port),如果连接出错,返回socket.error错误。元组的第一个参数是ip,可以是ip或域名,第一个数是端口号,它们是套接字。 |
socket.connect_ex() | connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 |
公共API | 公共用途的套接字函数 |
socket.recv() | 接收TCP数据,数据以字符串形式返回,有两个参数,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。 |
socket.send() | 发送TCP数据,将参数作为封装为TCP数据包发送到连接的套接字。返回值是要发送的字节数量。参数只能是字节,其他类型是高级语言特有的,计算机无法解析,将其他类型转化为字节encode(‘utf-8’)才能发送。 |
socket.sendall() | 完整发送TCP数据,完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。 |
socket.recvfrom() | 接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。创建scoket时type参数要对应。 |
socket.sendto() | 发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。 |
socket.close() | 关闭套接字 |
socket.getpeername() | 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port) |
socket.getsockname() | 返回套接字自己的地址。通常是一个元组(ipaddr,port) |
socket.setsockopt(level,optname,value) | 设置给定套接字选项的值 |
socket.getsockopt(level,optname[.buflen]) | 返回套接字选项的值。 |
socket.settimeout(timeout) | 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect()) |
socket.gettimeout() | 返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。 |
socket.fileno() | 返回套接字的文件描述符 |
socket.setblocking(flag) | 如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。 |
socket.makefile() | 创建一个与该套接字相关连的文件 |
简单TCP案例
服务端:
import socket
# 创建socket,网络协议为ipv4,网络层协议为TCP
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定套接字,接收所有ip
s.bind(('0.0.0.0',9000))
# 监听端口,指定连接最大数量
s.listen(2)
print("is listening...")
# 等待客户端连接,返回服务端scoket连接和套接字
fd, addr = s.accept()
print("connect to client successful")
# 接收客户端数据
data = fd.recv(1024)
print(data)
# 响应客户端
fd.send("Welcome to connect !".encode("utf-8"))
# 关闭连接
s.close()
print("closed")
客户端:
import socket
# 创建socket
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立连接
s.connect(("127.0.0.1",9000))
# 发送数据
s.send("hello".encode("utf-8"))
print("connect to server successful")
# 接收响应数据
data = s.recv(1024)
print(data.decode("utf-8"))
# 关闭连接
s.close()
print("closed")
TCP协议的
三次握手
建立连接,所以第一次必须由客户端发送连接请求,因此服务端必须要在客户端先开启服务。
服务器端程序
import socket
s = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
s.bind(('0.0.0.0', 9000))
s.listen(1)
print("is listening...")
fd, addr = s.accept()
print(fd.getpeername(), "is connected")
fd.send("welcome to connect !".encode('utf-8'))
def send_msg():
print("请输入:")
data0 = input()
fd.send(data0.encode("utf-8"))
def recv_meg():
data1 = fd.recv(1024)
print("收到客户端消息:", data1)
if __name__ == "__main__":
while True:
# 接收消息
recv_meg()
# 发送消息
send_msg()
print("已发送")
客户端程序
import socket
s = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
s.connect(('127.0.0.1', 9000))
print("连接成功!")
def make_sock():
send_data=input()
s.send(send_data.encode("utf-8"))
print("已发送!")
return None
def recv_scok():
get_data = s.recv(1024)
if get_data is None:
print("未收到数据")
else:
print("收到服务端内容:")
print(get_data)
return None
if __name__ == "__main__":
flag = 1
while True:
if flag == 0:
s.close()
break
else:
print("请输入:")
make_sock()
# 接收消息
recv_scok()
print("请选择:任意.继续聊天 0.退出")
a = input()
flag = a
参考资料:
https://www.cnblogs.com/yunlong-study/p/9283529.html
https://www.cnblogs.com/zhangyingai/p/7097922.html
https://www.cnblogs.com/zhangxinqi/p/8243919.html
http://www.runoob.com/python3/python3-socket.html
UDP
udp和tcp差不多,只是udp使无连接无状态的,不需要监听,只有接收和发送数据。可能造成数据丢失。
# 客户端
import socket
s = socket.socket(family=socket.AF_INET,type=socket.SOCK_DGRAM)
# 发送数据
s.sendto("hell0".encode("utf-8"),('127.0.0.1',9000))
# 接收数据
data = s.recv(1024)
print(data)
if __name__ == "__main__":
while True:
print("请输入:")
put = input()
s.sendto(put.encode("utf-8"),('127.0.0.1',9000))
print("收到服务端的数据:",s.recvfrom(1024))
# 服务器端
import socket
s = socket.socket(family=socket.AF_INET,type=socket.SOCK_DGRAM)
# 绑定端口
s.bind(("127.0.0.1",9000))
# 接收数据
data = s.recvfrom(1024)
print("收到来自客户端消息:",data)
# 发送数据
s.sendto("send a message to you".encode("utf-8"),('127.0.0.1',9000))
if __name__ == "__main__":
while True:
print("请输入:")
put = input()
s.sendto(put.encode("utf-8"),('127.0.0.1',9000))
print("收到客户端的数据:",s.recvfrom(1024))
udp协议程序只管发送和接收数据,如果接收端没有udp程序接收数据就丢失了。
在主机上测试,发送端是本身,接收端也是本省,客户端之气启动作用,数据都在服务端。