01: socket模块

网络编程其他篇

目录:

1.1 socket理论部分返回顶部

  1、socket起源

      1. socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。

      2. socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)

      3. 基本上,Socket 是任何一种计算机网络通讯中最基础的内容

      4. 例如当你在浏览器地址栏中输入 http://www.cnblogs.com/ 时,你会打开一个套接字,然后连接到 http://www.cnblogs.com/ 并读取响应的页面然后然后显示出来

      5. socket和file的区别:

        1)  file模块是针对某个指定文件进行【打开】【读写】【关闭】

        2)  socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】

  2、socket.socket( family, type )  实例化一个socket类

    1. 实例化需要3个参数

        1)  第一个是地址簇(默认是socket.AF_INET)

        2)  第二个是流(socket.SOCK_STREAM, 默认值)或数据报(socket.SOCK_DGRAM)套接字

        3)  第三个是实用的协议(默认是0 使用默认即可),对于一个普通的套接字不需要提供任何参数

    2. 第一个参数:  Socket Families(地址簇)  类似于OSI七层的网络层

        1socket.AF_UNIX     unix本机进程间通信(本机没有网卡时本机件进程自己起socket通信)

        2)socket.AF_INET   IPV4 

        3)socket.AF_INET6  IPV6

    3. 第二个参数:  Socket Families(地址簇)  类似于OSI七层的传输层

        1)socket.SOCK_STREAM   #for tcp

        2)socket.SOCK_DGRAM    #for udp

        3)socket.SOCK_RAW    #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;

            其次,SOCK_RAW也可以处理特殊的IPv4报文;此外利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头

        4)socket.SOCK_RDM    #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议低级访问,

            在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用

  3、socket对象可以使用的所有方法

# 1、 sk.bind(address)
   s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。 # 2、 sk.listen(backlog)
   开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。
backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
这个值不能无限大,因为要在内核中维护连接队列 # 3、 sk.setblocking(bool)
   是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。 # 4、 sk.accept()
   接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。
   接收TCP 客户的连接(阻塞式)等待连接的到来 # 5、 sk.connect(address)
   连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 # 6、 sk.connect_ex(address)
   同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061 # 7、 sk.close()
   关闭套接字 # 8、 sk.recv(bufsize[,flag])
   接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。 # 9、 sk.recvfrom(bufsize[.flag])
   与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 # 10、 sk.send(string[,flag])
   将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。 # 11、 sk.sendall(string[,flag])
   将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
内部通过递归调用send,将所有内容发送出去。 # 12、 sk.sendto(string[,flag],address)
   将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。 # 13、 sk.settimeout(timeout)
   设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s ) # 14、 sk.getpeername()
   返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。 # 15、 sk.getsockname()
   返回套接字自己的地址。通常是一个元组(ipaddr,port) # 16、 sk.fileno()
   套接字的文件描述符

socket对象可以使用的所有方法总结

  4、TCP三层握手

####1、第一次握手
# 建立连接时,客户端发送SYN包到服务器,其中包含客户端的初始序号seq=x,并进入SYN_SENT状态,等待服务器确认。 ####2、第二次握手
# 服务器收到请求后,必须确认客户的数据包。同时自己也发送一个SYN包,即SYN+ACK包,此时服务器进入SYN_RECV状态。 ####3、第三次握手
# 客户端收到服务器的SYN+ACK包,向服务器发送一个序列号(seq=x+1),确认号为ack(客户端)=y+1,此包发送完毕,
# 客户端和服务器进入ESTAB_LISHED(TCP连接成功)状态,完成三次握手。

三次握手

#### 1、第一次挥手
# 首先,客户端发送一个FIN,用来关闭客户端到服务器的数据传送,然后等待服务器的确认。其中终止标志位FIN=1,序列号seq=u。 #### 2、第二次挥手
# 服务器收到这个FIN,它发送一个ACK,确认ack为收到的序号加一。 #### 3、第三次挥手
# 关闭服务器到客户端的连接,发送一个FIN给客户端。 #### 4、第四次挥手
# 客户端收到FIN后,并发回一个ACK报文确认,并将确认序号seq设置为收到序号加一。
# 首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

四次挥手

    01: socket模块

1.2 socket处理单个连接 和 同时接受多个连接返回顶部

  1、socket通信原理图解

01: socket模块

  2、简单的socket列子:处理单个连接

import socket
#服务器端
server = socket.socket() # 创建一个socket对象
server.bind(('',6969)) # host=’ ‘或者‘0.0.0.0’都表示所有计算机都可以访问服务器
server.listen() # server.listen(5) 表示同时可以有五个客户连接到服务器 conn,addr = server.accept() # c, addr = s.accept() 这是一种赋变量的方法,会组成一个元组。
# 代表只要客户端提供地址(addr)就可以接受外部的链接
data = conn.recv(1024) # 收到客户端数据:recv默认是阻塞的,收到数据为空则死循环
conn.send(data.upper()) # 服务器返回数据给client端 server.close()

处理单个连接: server部分

import socket
#客户端
client = socket.socket() # 创建socket对象
client.connect(('localhost',6969)) # 连接到服务端
client.send("在python3中socket仅可以传送byte类型".encode("utf-8")) # 发送数据到服务端
data = client.recv(1024) # 接收服务端返回数据 print('客户端收到返回数据为:',data.decode())
client.close()

处理单个连接: client部分

  3、socket实现同时连接多个客户端

      1. 这里仅能实现,同时连接多个客户端,但同一时刻仅能有一个客户端与服务的通信,其他客户端阻塞状态

      2. 只有当前一个客户端断开连接后,后面的客户端才能与服务端发消息

      3. 经测试,只能在Linux服务器中实现,在Windows中,断开客户端后服务端就会应发异常

#-*- coding:utf-8 -*-
import socket
#服务器端python3.5 server = socket.socket()
server.bind(('',6969)) #绑定要监听的端口
server.listen(5) #我要监听这个端口,最大连接数为:5
print("我要开始等电话了")
while True: #当一个客户端断开后程序就回到这里,等待下一个连接
conn,addr = server.accept() #等电话打进来 conn是打电话实例,addr电话号码
#conn就是客户端连过来而在服务器端为其生成的连接实例,这里其实就是为每个客户端生成一个实例区分
while True:
data = conn.recv(1024) #data是接收到的从客户端发送过来的数据
print("rect:",data) #将客户端发送过来的数据在服务器端打印
if not data:
#如果客户端断开连接,data就为空,就跳出内层while循环,到达外层循环等待下一个客户端连接
print("host has lost!!!")
break
conn.send(data.upper()) #将客户端发送来的数据变成大写,然后再发送给客户端
server.close()

同时连接多个客户端server端代码

#-*- coding:utf-8 -*-
import socket
#客户端python3.5 client = socket.socket() #声明socket类型,同时生成socket连接对象
client.connect(('1.1.1.3',6969)) #指定要连接的服务器地址和端口号 while True:
msg = input(">>:").strip() #输入要发送给服务器的消息
if len(msg) == 0:continue #如果客户端输入空格,让客户端继续输入
client.send(msg.encode("utf-8")) #将输入的消息发送到服务器端
data = client.recv(1024) #接收服务器端发送过来的数据,每次1024字节
print('client_recv:',data) #将从服务器端接收的数据在客户端打印出来
client.close()

同时连接多个客户端client端代码

1.3 socket实现远程执行命令,下载文件返回顶部

   1、使用socket实现远程执行命令

#-*- coding:utf-8 -*-
import socket,os #服务器端python3.5
server = socket.socket()
server.bind(('1.1.1.3',6969)) #绑定要监听的端口
server.listen(5) #我要监听这个端口
print("我要开始等电话了")
while True:
conn,addr = server.accept() #等电话打进来 conn是打电话实例,addr电话号码
while True:
#conn就是客户端连过来而在服务器端为其生成的连接实例
data = conn.recv(1024)
data = data.decode()
print("接收的命令:",data)
if not data:
print("host has lost!!!")
break
res = os.popen(data).read()
conn.send(res.encode("utf-8"))
server.close()

执行命令: server端

#-*- coding:utf-8 -*-
import socket
#客户端python3.5
client = socket.socket() #声明socket类型,同时生成socket连接对象
client.connect(('1.1.1.3',6969)) while True:
msg = input(">>:").strip()
client.send(msg.encode("utf-8"))
data = client.recv(1024) print('客户端接收到服务端命令结果:',data.decode())
client.close()

执行命令: client端

  2、 socket实现下载文件

      1)先在服务器上把要传送的视屏文件打开,用send发送给客户端
      2)在客户端上打开一个新文件,将收到数据写入到文件中
      3)测试结果是:在客户端每次输入任意符号服务器端就会发送一个1024字节的文件到客户端
      4)可以看到的结果是,文件以1024的倍数增加

import socket,os
#socket服务器端
server = socket.socket()
server.bind(('0.0.0.0',2222))
server.listen(5)
while True:
conn,addr = server.accept()
while True:
file_name = conn.recv(1024).decode()
file_size = os.stat(file_name).st_size
conn.send(str(file_size).encode("utf-8"))
with open(file_name,'rb') as f:
for line in f:
conn.send(line)
server.close()

socket下载文件: server端

import  socket
#socket客户端
client = socket.socket()
client.connect(('1.1.1.3',2222))
while True:
file_name = input("please input you need to download file:")
client.send(file_name.encode("utf-8"))
file_size = client.recv(1024).decode()
print(type(file_size),file_size)
file_size = int(file_size)
recv_size = 0
with open(file_name + '.new','wb') as f:
while recv_size < file_size:
data = client.recv(1024)
f.write(data)
recv_size += 1024
client.close()

socket下载文件: client端代码

1.4 通过socket实现简单的ssh 和 处理连包问题返回顶部

   1、说明

      1. 先运行服务器端,再运行客户端,客户端连接后发送数据给服务器端,服务器端将结果返回给客户端
      2. 这段代码在windows python3.5中运行和 centos6.5系统中运行都正常
      3. 下面用Windows作为客户端,centos6.5作为服务器端演示操作结果
      4. 运行发现如果客户端服务端都在centos中运行时,客户端断开服务器端会继续监听下一个连接
      5. 但是如果客户端是Windows无论服务器端是什么系统客户端断开服务器端就会自动断开

   2、socket实现ssh远程执行命令 及 处理连包问题

import socket,os
#ssh服务器端代码 server = socket.socket()
server.bind(('0.0.0.0',9999)) #0.0.0.0表示允许所有主机连接
server.listen()
print("服务器端ssh开始监听客户端的链接啦!!") while True:
conn,addr = server.accept() #每次客户端断开,就会到这里等待新的连接
while True:
print("等待新指令!!")
client_cmd = conn.recv(1024).decode() #服务器端接收客户端发送过来的命令
if not client_cmd: #1 如果客户端断开连接收到的数据为空就会卡在这里
print("客户端已断开连接")
break
print("执行命令:",client_cmd)
cmd_res = os.popen(client_cmd).read() #接收字符串,执行结果也是字符串
print("before send:",len(cmd_res)) #打印出命令执行结果的长度
if len(cmd_res) == 0: #2 如果命令错误则执行结果长度为 0
cmd_res = "The command is wrong!!"
conn.send( str(len(cmd_res.encode())).encode("utf-8") ) #发送命令结果长度
client_ack = conn.recv(1024) #3 为了解决连包问题可以在两个send中间加一个recv
conn.send(cmd_res.encode("utf-8")) #将命令执行结果发送给客户端
print("send done")
server.close()

ssh服务器: server端代码

import socket
#ssh 客户端程序代码 client = socket.socket()
client.connect(('localhost',9999)) while True:
cmd = input("请输入要执行的命令:")
if len(cmd) == 0:continue #1 客户端输入命令为空就不发送让他继续输入命令
client.send(cmd.encode("utf-8")) #将要执行命令发送到服务器
cmd_res_size = client.recv(1024).decode() #接收命令结果的长度
print("执行命令结果的长度:",cmd_res_size) #打印要接收结果的数据长度
client.send('ok'.encode('utf-8')) #2 为解决连包问题可以在连续两次send中间加一个recv
received_size = 0 #接收数据的长度初始值为 0
received_data = b'' #接收到的数据初始值为 空字符串
while received_size < int(cmd_res_size):
data = client.recv(1024) #每次从服务器端接收1024的数据
received_size += len(data) #接收到的数据长度累加
received_data += data #每次接收的数据累加到 received_data变量中
else:
print("The command receive done:",received_size) #打印最终接收的长度
print(received_data.decode()) #将接收到的命令执行结果打印出来
client.close() #1 在centos6.5 10.1.0.50服务器端运行结果:
[root@localhost python]# python35 ssh_server.sh
服务器端ssh开始监听客户端的链接啦!!
等待新指令!!
执行命令: df
before send: 429
send done
等待新指令!!
#2 在Windows 7中运行客户端的结果:
请输入要执行的命令:df
执行命令结果的长度: 429
The command receive done: 429
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/VolGroup-lv_root 38744716 4118012 32658576 12% /
tmpfs 510148 224 509924 1% /dev/shm
/dev/sda1 495844 34846 435398 8% /boot
/dev/sr0 4363088 4363088 0 100% /media/CentOS_6.5_Final
/dev/sr0 4363088 4363088 0 100% /mnt

ssh 客户端: client端代码

上一篇:SpringMVC云题库错题及答案汇总-2


下一篇:《SQL Server性能调优实战》知识点汇总