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设置为收到序号加一。
# 首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
四次挥手

    

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

  1、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端代码

 

posted @ 2017-11-27 09:17  不做大哥好多年  阅读(337)  评论(0编辑  收藏  举报