1、Socket(也称套接字)介绍
socket这个东东干的事情,就是帮你把tcp/ip协议层的各种数据封装啦、数据发送、接收等通过代码已经给你封装好了
,你只需要调用几行代码,就可以给别的机器发消息了。
参考https://www.cnblogs.com/weizhixiang/p/6298523.html
2、套接字
所谓socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。应用程
序通常通过"套接字"向网络发出请求或者应答网络请求。
套接字可以根据通信性质分类,这种性质对于用户是可见的。应用程序一般仅在同一类的
套接字间进行通信。不过只要底层的通信协议允许,不同类型的套接字间也照样可以通信。套
接字有两种不同的类型:流套接字和数据报套接字。
3、套接字的工作原理
要通过互联网进行通信,你至少需要一对套接字,其中一个运行于客户机端,我们称之为
ClientSocket,另一个运行于服务器端,我们称之为ServerSocket。
根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个
步骤:服务器监听,客户端请求,连接确认。
所谓服务器监听,是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的
状态,实时监控网络状态。
所谓客户端请求,是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接
字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的
地址和端口号,然后就向服务器端套接字提出连接请求。
所谓连接确认,是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它
就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦
客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他
客户端套接字的连接请求。
4、网络编程的解释
网络编程的目的就是指直接或间接地通过网络协议与其他计算机进行通讯。网络编程中
有两个主要的问题,一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后
如何可靠高效的进行数据传输。在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的
路由,由IP地址可以唯一地确定Internet上的一台主机。而TCP层则提供面向应用的可靠的
或非可靠的数据传输机制,这是网络编程的主要对象,一般不需要关心IP层是如何处理数据
的。
目前较为流行的网络编程模型是客户机/服务器(C/S)结构。即通信双方一方作为服务
器等待客户提出请求并予以响应。客户则在需要服务时向服务器提出申请。服务器一般作为
守护进程始终运行,监听网络端口,一旦有客户请求,就会启动一个服务进程来响应该客
户,同时自己继续监听服务端口,使后来的客户也能及时得到服务。
在Internet上IP地址和主机名是一一对应的,通过域名解析可以由主机名得到机器的IP,
由于机器名更接近自然语言,容易记忆,所以使用比IP地址广泛,但是对机器而言只有IP地
址才是有效的标识符。
通常一台主机上总是有很多个进程需要网络资源进行网络通讯。网络通讯的对象准确的讲
不是主机,而应该是主机中运行的进程。这时候光有主机名或IP地址来标识这么多个进程显然
是不够的。端口号就是为了在一台主机上提供更多的网络资源而采取得一种手段,也是TCP层
提供的一种机制。只有通过主机名或IP地址和端口号的组合才能唯一的确定网络通讯中的对象:
进程。
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。
在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部。
5、编写Socket套接字的流程
1、服务器端程序的编写步骤:
(1)创建调用socket()函数创建一个用于通信的套接字。#phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
(2)s.bind() 绑定(主机,端口号)到套接字。 #phone.bind(('127.0.0.1',8080))
(3)s.listen() 开始TCP监听 。#phone.listen(5)# 5 代表最多挂机数
(4)s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来。# conn,client_addr = phone.accept()
(5)处理客户端的连接请求。 # 1、s.recv() 接收数据 /data = conn.recv(1024) # 2、反馈给客户端:s.send() 发送数据
(6)关闭套接字/ s.send() 发送数据
2、客户端程序的编写步骤:
(1)调用socket()函数创建一个用于通信的套接字。# phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
(2)s.connect() 主动初始化TCP服务器连接 # phone.connect(('127.0.0.1',8080))
(3)调用读写函数发送或者接收数据。# s.send() 发送数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完,可后面通过实例解释)
例如:phone.send('hello'.encode('utf-8'))
(4)接受服务端的反馈信息: s.recv() 接收数据 # 例如:
data = phone.recv(1024)
(5)关闭sokect
参考:https://www.cnblogs.com/lixiaoliuer/p/6543968.html
② server.bind(address) server.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址 ③ server.listen(backlog) 开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5,这个值不能无限大,因为要在内核中维护连接队列 ④ server.setblocking(bool) 是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错 ⑤ conn,addr = server.accept() 接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。接收TCP 客户的连接(阻塞式)等待连接的到来 ⑥ client.connect(address) 连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 ⑦ client.connect_ex(address) 同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061 ⑧ client.close() 关闭套接字 ⑨ client.recv(bufsize[,flag]) 接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略 ⑩ client.recvfrom(bufsize[.flag]) 与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址 ⑪ server.send(string[,flag]) 将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送 ⑫ server.sendall(string[,flag]) 将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常; 内部通过递归调用send,将所有内容发送出去 ⑬ server.sendto(string[,flag],address) 将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议 ⑭ sk.settimeout(timeout) 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s ) ⑮ sk.getpeername() 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port) ⑯ sk.getsockname() 返回套接字自己的地址。通常是一个元组(ipaddr,port) ⑰ sk.fileno() 套接字的文件描述符
6、代码实例
6.1、简单的套接字通信
服务端和客户端在一个IDE下
服务端:
import socket # 1 买手机 # phone 为套接字对象 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#(sock_stream 流) # 2 绑定手机卡,127.0.0.1是回送地址,指本地机,一般用来测试使用。客户端、服务端在一台主机上 phone.bind(('127.0.0.1',8081))# 端口 0-65535:0-1024给操作系统使用 # 3 开机 phone.listen(5)# 5 代表最多挂机数 # 4 等电话连接 # 在同一个IDE上执行客户端、服务端,,先执行服务端后,在执行客户端 # 直接执行客户端会发生错误 print('------starting-------') conn,client_addr = phone.accept() # accept()对于 connect()做的3次握手 # print('==========>') # print(res) # 5 收、发消息 data = conn.recv(1024) # 1024个字节 代表接受数据最大数,,单位bytes字节 print('客户端数据',data) conn.send(data.upper()) # 服务端回数据给客户端 # 6 挂电话 conn.close() # 7 关机 phone.close()
客户端:
import socket # 1 买手机 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#(sock_stream 流) # 2 拨号,127.0.0.1是回送地址,指本地机,一般用来测试使用。客户端、服务端在一台主机上 phone.connect(('127.0.0.1',8081))# 端口 0-65535:0-1024给操作系统使用 # 3 发、收 消息 phone.send('hello'.encode('utf-8')) data = phone.recv(1024) print(data) # 4 关闭 phone.close()
服务端先执行,一直开着,然后再执行客户端
6.2、加上链接循环
6.3、简单的远程执行命令程序开发
#-----------windows----------------- #dir:查看某一个文件夹下的子文件名与子文件夹名 #ipconfig:查看本地网卡的ip信息 #tasklist:查看运行的进程 #----------linux---------------: #ls #ifconfig #ps aux #执行系统命令,并且拿到命令的结果 # import os # res=os.system('xxxxlxxxs /') # print('命令的结果是:',res) import subprocess obj=subprocess.Popen('xxxxxxls /',shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) print(obj) print('stdout 1--->: ',obj.stdout.read().decode('utf-8')) # print('stdout 2--->: ',obj.stdout.read().decode('utf-8')) #print('stderr 1--->: ',obj.stderr.read().decode('utf-8'))
服务端:
import socket import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(('127.0.0.1',9901)) #0-65535:0-1024给操作系统使用 phone.listen(5) print('starting...') while True: # 链接循环 conn,client_addr=phone.accept() print(client_addr) while True: #通信循环 try: #1、收命令 cmd=conn.recv(1024) if not cmd:break #适用于linux操作系统 #2、执行命令,拿到结果 obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout=obj.stdout.read() stderr=obj.stderr.read() #3、把命令的结果返回给客户端 print(len(stdout)+len(stderr)) conn.send(stdout+stderr) #+是一个可以优化的点 except ConnectionResetError: #适用于windows操作系统 break conn.close() phone.close()
客户端:
import socket phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.connect(('127.0.0.1',9901)) while True: #1、发命令 cmd=input('>>: ').strip() #ls /etc if not cmd:continue phone.send(cmd.encode('utf-8')) #2、拿命令的结果,并打印 data=phone.recv(1024) print(data.decode('gbk'))# window用gbk phone.close()