网络编程----socket套接字

socket套接字

一, 操作系统基础

  • 操作系统:(Operating System,简称OS)是管理和控制计算机硬件与软件资源的计算机程序,是直接运行在“裸机”上的最基本的系统软件,任何其他软件都必须在操作系统的支持下才能运行.
  • 精简的说的话,操作系统就是一个协调,管理和控制计算机硬件资源和软件资源的控制程序.
  • 操作系统位于计算机硬件与应用软件之间,本质也是一个软件.操作系统由操作系统的内核(运行于内核态,管理硬件资源)以及系统调用(运行于用户态,为应用程序员写的应用程序提供系统调用接口)两部分组成,所以,单纯的说操作系统是运行于内核态的,是不准确的

二, 为什么学习socket

  • socket就是网络通信工具,也就是套接字,任何一门语言都有socket,他不是任何一门语言的专有名词,而是大家通过自己的程序与其他的电脑进行网络通信的时候都用它,用自己的电脑和别人的电脑将进行联系并发送消息或者文件操作等就叫做网络通信
  • 避免学习各层的接口,以及协议的使用,socket已经封装好了所有的接口,直接使用这些接口或者方法即可,提升开发效率
  • socket在python就是一个模块,通过使用模块提供的功能,建立客户端与服务端的通信,使用方便

三, socket套接字的工作流程

一, TCP协议下的socket

  1. 服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接.在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了.客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束

  • 细说socket()模块函数用法
import socket
socket.socket(socket_family,socket_type,protocal=0)
 socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默认值为 0。

 获取tcp/ip套接字
tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

获取udp/ip套接字
udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

由于 socket 模块中有太多的属性。我们在这里破例使用了'from module import *'语句。使用 'from socket import *',我们就把 socket 模块里的所有属性都带到我们的命名空间里了,这样能 大幅减短我们的代码。
例如tcpSock = socket(AF_INET, SOCK_STREAM)
服务端套接字函数
s.bind()    绑定(主机,端口号)到套接字
s.listen()  开始TCP监听
s.accept()  被动接受TCP客户的连接,(阻塞式)等待连接的到来

客户端套接字函数
s.connect()     主动初始化TCP服务器连接
s.connect_ex()  connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

公共用途的套接字函数
s.recv()            接收TCP数据
s.send()            发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
s.sendall()         发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
s.recvfrom()        接收UDP数据
s.sendto()          发送UDP数据
s.getpeername()     连接到当前套接字的远端的地址
s.getsockname()     当前套接字的地址
s.getsockopt()      返回指定套接字的参数
s.setsockopt()      设置指定套接字的参数
s.close()           关闭套接字

面向锁的套接字方法
s.setblocking()     设置套接字的阻塞与非阻塞模式
s.settimeout()      设置阻塞套接字操作的超时时间
s.gettimeout()      得到阻塞套接字操作的超时时间

面向文件的套接字的函数
s.fileno()          套接字的文件描述符
s.makefile()        创建一个与该套接字相关的文件
  • 单个客户端与服务端通信
# 模拟通话
# server.py服务器文件
import socket

# phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone = socket.socket()  # 默认是基于网络类型,TCP
phone.bind(('192.168.137.1', 8080))  # 绑定ip和端口
phone.listen(5)  # 同一时刻可以接收5个请求,但是可以有N多个链接
conn, addr = phone.accept()  # 接收链接请求
# conn: 链接通道   addr: 客户端的ip和端口
from_client = conn.recv(1024)  # 一次接收的最大限制  bytes
print(from_client.decode('utf-8'))
conn.send('你是不是傻'.encode('utf-8'))  # 回复
conn.close()   # 关闭通道
phone.close()  # 关闭系统
------------------------------------------------------
# client.py客户端文件
import socket
phone = socket.socket()
phone.connect(('192.168.137.1', 8080))  # 请求与服务器端建立链接

phone.send('hello'.encode('utf-8'))
from_server = phone.recv(1024)
print(from_server.decode('utf-8'))
phone.close()
  • 通信循环
# server.py服务器文件
import socket

phone = socket.socket()
phone.bind(('192.168.137.1', 8080))
phone.listen(5)
conn, addr = phone.accept()

while 1:  # 循环收发消息
    try:
        from_client = conn.recv(1024)
        print(from_client.decode('utf-8'))

        conn.send('你是不是傻'.encode('utf-8'))
    except ConnectionResetError:
        break
conn.close()
phone.close()
-----------------------------------------------------
# client.py客户端文件
import socket
phone = socket.socket()
phone.connect(('192.168.137.1', 8080))  # 请求与服务器端建立链接

while 1:
    to_server = input('>>>:')
    if to_server.upper() == 'Q':
        break
    phone.send(to_server.encode('utf-8'))
    from_server = phone.recv(1024)
    print(from_server.decode('utf-8'))
phone.close()
  • 通信,连接循环
# server.py服务器文件
import socket

phone = socket.socket()
phone.bind(('192.168.137.1', 8080))
phone.listen(5)

while 1:  # 循环链接客户端
    conn, addr = phone.accept()
    while 1:  # 循环收发消息
        try:
            from_client = conn.recv(1024)
            print(from_client.decode('utf-8'))

            conn.send('你是不是傻'.encode('utf-8'))
        except ConnectionResetError:
            break
    conn.close()

-----------------------------------------------------
# client.py客户端文件
import socket
phone = socket.socket()
phone.connect(('192.168.137.1', 8080))  # 请求与服务器端建立链接

while 1:
    to_server = input('>>>:')
    if to_server.upper() == 'Q':
        break
    phone.send(to_server.encode('utf-8'))
    from_server = phone.recv(1024)
    print(from_server.decode('utf-8'))
phone.close()
  • 远程执行命令
# server.py服务器文件
import socket
import subprocess

phone = socket.socket()
phone.bind(('192.168.137.1', 8080))
phone.listen(5)

while 1:  # 循环链接客户端
    conn, addr = phone.accept()
    while 1:  # 循环收发消息
        try:
            cmd = conn.recv(1024)
            ret = subprocess.Popen(cmd.decode('utf-8'),
                                   shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            correct_msg = ret.stdout.read()   # 正确命令执行结果
            error_msg = ret.stderr.read()     # 错误命令报错信息
            to_client = correct_msg + error_msg
            conn.send(to_client)
        except ConnectionResetError:
            break
    conn.close()
---------------------------------------------------------
# client.py客户端文件
import socket
phone = socket.socket()
phone.connect(('192.168.137.1', 8080))  # 请求与服务器端建立链接

while 1:
    to_server = input('>>>:')
    if to_server.upper() == 'Q':
        break
    phone.send(to_server.encode('utf-8'))
    from_server = phone.recv(1024)
    print(from_server.decode('gbk'))  # subprocess在windows默认用gbk编码
phone.close()
  • 详解recv的工作原理
'''
源码解释:
Receive up to buffersize bytes from the socket.
接收来自socket缓冲区的字节数据,
For the optional flags argument, see the Unix manual.
对于这些设置的参数,可以查看Unix手册。
When no data is available, block untilat least one byte is available or until the remote end is closed.
当缓冲区没有数据可取时,recv会一直处于阻塞状态,直到缓冲区至少有一个字节数据可取,或者远程端关闭。
When the remote end is closed and all data is read, return the empty string.
关闭远程端并读取所有数据后,返回空字符串。
'''
----------------------------------------------------------
# 服务端
# 1,验证服务端缓冲区数据没有取完,又执行了recv,recv会继续取值
import socket

phone =socket.socket()
phone.bind(('127.0.0.1',8080))
phone.listen(5)

conn, client_addr = phone.accept()
from_client_data1 = conn.recv(2)
print(from_client_data1)
from_client_data2 = conn.recv(2)
print(from_client_data2)
from_client_data3 = conn.recv(1)
print(from_client_data3)
conn.close()
phone.close()

# 客户端
import socket
phone = socket.socket()
phone.connect(('127.0.0.1',8080))
phone.send('hello'.encode('utf-8'))
time.sleep(20)

phone.close()
----------------------------------------------------------
# 服务端
# 2,验证服务端缓冲区取完了,又执行了recv,此时客户端20秒内不关闭的前提下,recv处于阻塞状态

import socket

phone =socket.socket()
phone.bind(('127.0.0.1',8080))
phone.listen(5)

conn, client_addr = phone.accept()
from_client_data = conn.recv(1024)
print(from_client_data.decode('utf-8'))
conn.recv(1024) # 此时程序阻塞20秒左右,因为缓冲区的数据取完了,并且20秒内,客户端没有关闭
print(222)

conn.close()
phone.close()

# 客户端
import socket
import time
phone = socket.socket()
phone.connect(('127.0.0.1',8080))
phone.send('hello'.encode('utf-8'))
time.sleep(20)

phone.close()
----------------------------------------------------------
# 服务端
# 3,验证服务端缓冲区取完了,又执行了recv,此时客户端处于关闭状态,则recv会取到空字符串

import socket

phone =socket.socket()
phone.bind(('127.0.0.1',8080))
phone.listen(5)

conn, client_addr = phone.accept()
from_client_data1 = conn.recv(1024)
print(from_client_data1)
from_client_data2 = conn.recv(1024)
print(from_client_data2)
from_client_data3 = conn.recv(1024)
print(from_client_data3)
conn.close()
phone.close()

# 客户端
import socket

phone = socket.socket()
phone.connect(('127.0.0.1',8080))
phone.send('hello'.encode('utf-8'))
phone.close()

猜你喜欢

转载自www.cnblogs.com/douzi-m/p/11976626.html