Python开发——12.socket编程

一、OSI七层

1.物理层

物理层的主要功能是基于电气特性发送高低电压(高代表1,低代表0)形成电信号,使计算机完成组网以达到接入Internet的目的

2.数据链路层

数据链路层是用来定义电信号的分组方式,使单纯的电信号0和1变得有意义

(1)以太网协议

以太网协议(ethernet)是统一的分组标准,以太网协议规定:a.一组电信号构成一个数据包,叫做“帧”;b。每一数据帧分成报头head和数据data两部分

报头(head)固定为18个字节,发送者即源地址、接受者即目标地址和数据类型各占6个字节

数据(data)最短为46字节,最长为1500字节,它包含了数据包的具体内容

(2)mac地址和广播

ethernet规定接入Internet的设备都必须具备网卡,发送端和接收端的地址即网卡地址,又称mac地址。

ethernet采用广播的方式进行通信,一台主机通过ARP协议获取另一台主机的mac地址

3.网络层

网络层引入了一套新的地址即网络地址来区分不同的广播域

(1)IP协议

IP地址规定了网络地址由32位2进制表示,即从0.0.0.0-255.255.255.255,IP地址分为主机部分和网络部分

(2)子网掩码

子网掩码是描述子网络特征的参数,形式上与IP地址一致,但网络部分全部为1,主机部分全部为0,利用子网掩码与IP地址进行and运算(两个位数都为1运算结果为1,否则为0),结果相同表明在同一个子网络中。

(3)ARP协议

通过广播的方式发送数据包来获取目标主机的mac地址

4.传输层

传输层是用来建立端口到端口的通信,端口范围为0-65535,其中0-1023为系统占用端口

(1)tcp协议和udp协议

TCP协议为可靠传输,通常TCP的数据包的长度不会超过IP数据包的长度,理论上可以无限长;UDP协议为不可靠传输,总长度不超过65535个字节

(2)tcp协议的三次握手和四次挥手

5.应用层

用来规定开发的应用程序的数据格式

二、socket

1.客户端/服务端架构

即client/server(C/S)架构,socket就是为了实现C/S架构的开发,C/S架构的软件是基于网络进行通信的。

2.socket层

3.socket定义

socket是应用层与TCP/IP协议通信的中间层,是一组封装了TCP/IP协议的接口,用户遵循socket规定编程即遵循tcp/udp协议

socket主要有基于文件类型(AF_UNIX)和基于网络类型(AF_INET)两种家族。

4.工作流程

5.基于TCP的套接字

tcp是基于链接的,必须先启动服务端

服务端

ss = socket() #创建服务器套接字
ss.bind()      #把地址绑定到套接字
ss.listen()      #监听链接
inf_loop:      #服务器无限循环
    cs = ss.accept() #接受客户端链接
    comm_loop:         #通讯循环
        cs.recv()/cs.send() #对话(接收与发送)
    cs.close()    #关闭客户端套接字
ss.close()        #关闭服务器套接字(可选)

from socket import *
import subprocess
ip_port = ("10.10.27.37",8000)
back_log = 5
buffer_size = 1024

ftp_server = socket(AF_INET,SOCK_STREAM)
ftp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
ftp_server.bind(ip_port)
ftp_server.listen(back_log)

while True:
    conn,addr = ftp_server.accept()
    print("新的客户端链接",addr)
    while True:
        try:
            cmd = conn.recv(buffer_size)
            print("收到客户端的命令",cmd)
            if not cmd:
                break
            res = subprocess.Popen(cmd.decode("utf-8"),shell = True,
                                   stderr=subprocess.PIPE,
                                   stdout=subprocess.PIPE,
                                   stdin=subprocess.PIPE)
            err = res.stderr.read()
            if err:
                cmd_res = err
            else:
                cmd_res = res.stdout.read()
            conn.send(cmd_res)
        except Exception:
            break
    conn.close()

ftp_server

客户端

cs = socket()    # 创建客户套接字
cs.connect()    # 尝试连接服务器
comm_loop:        # 通讯循环
    cs.send()/cs.recv()    # 对话(发送/接收)
cs.close()            # 关闭客户套接字

from socket import *
ip_port = ("10.10.27.37",8000)
buffer_size = 1024

ftp_client = socket(AF_INET,SOCK_STREAM)
ftp_client.connect(ip_port)

while True:
    cmd = input(">>>:")
    if not cmd :
        continue
    if cmd == "quit":
        break
    ftp_client.send(cmd.encode("utf-8"))
    res = ftp_client.recv(buffer_size)
    print(res.decode("gbk"))
ftp_client.close()

ftp_client

6.基于udp的套接字

udp是无连接的,可以先启动任意一端

服务端

ss = socket()   #创建一个服务器的套接字
ss.bind()       #绑定服务器套接字
inf_loop:       #服务器无限循环
    cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送)
ss.close()                         # 关闭服务器套接字

from socket import *
ip_port = ("10.10.27.37",8001)
buffer_size = 1024

udp_server = socket(AF_INET,SOCK_DGRAM)
udp_server.bind(ip_port)

while True:
    data,addr =udp_sever.recvfrom(buffer_size)
    print(data,addr)
    udp_server.sendto(data.upper(),addr)

udp_server

客户端

cs = socket()   # 创建客户套接字
comm_loop:      # 通讯循环
    cs.sendto()/cs.recvfrom()   # 对话(发送/接收)
cs.close()                      # 关闭客户套接字

from socket import *
udp_client = socket(AF_INET,SOCK_DGRAM)
ip_port = ("10.10.27.37",8001)
buffer_size = 1024

while True:
    msg = input(">>>:")
    udp_client.sendto(msg.encode("utf-8"),ip_port)
    data,addr = udp_client.recvfrom(buffer_size)
    print(addr)
    print(data.decode("utf-8"))

udp_client

7.粘包

(1)tcp和udp区别

  • TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
  • UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的

(2)粘包现象

粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

  • 正常情况

  • 发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)

  • 接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

(3)解决粘包的方法

发送时:先发报头长度,再编码报头内容然后发送,最后发真实内容。

接收时:先收报头长度,用struct取出来,根据取出的长度收取报头内容,然后解码,反序列化,从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容

from socket import *
import subprocess,struct
ip_port = ("10.10.27.37",666)
back_log = 5
buffer_size = 1024

tcp_server = socket(AF_INET,SOCK_STREAM)
tcp_server.bind(ip_port)
tcp_server.listen(back_log)

while True:
    conn,addr = tcp_server.accept()
    print("新的客户端链接",addr)
    while True:
        #收
        try:
            cmd = conn.recv(buffer_size)
            if not cmd :break
            print("收到客户端的命令",cmd)

            #执行命令,得到的运行结果cmd_res
            res = subprocess.Popen(cmd.decode("utf_8"),shell=True,
                                   stderr = subprocess.PIPE,
                                   stdout=subprocess.PIPE,
                                   stdin=subprocess.PIPE)
            err = res.stderr.read()
            if err:
                cmd_res = err
            else:
                cmd_res = res.stdout.read()

            #发
            if not cmd_res:
                cmd_res="执行成功".encode("gbk")
            #解决粘包
            length = len(cmd_res)

            data_length = struct.pack("i",length)#struct模块需要了解
            conn.send(data_length)
            conn.send(cmd_res)
        except Exception:
            break

解决粘包server端

from socket import *
import struct
from functools import partial
ip_port = ("10.10.27.37",666)
back_log = 5
buffer_size = 1024

tcp_client = socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port)

while True:
    cmd = input(">>>").strip()
    if not cmd:continue
    if cmd =="quit":break

    tcp_client.send(cmd.encode("utf-8"))
    #解决粘包
    length_data = tcp_client.recv(4)
    length = struct.unpack("i",length_data)[0]

    # recv_msg ="".join(iter(partial(tcp_client.recv,buffer_size),b""))
    recv_size = 0
    recv_data = "b"
    while recv_size<length:
        recv_data=tcp_client.recv(buffer_size)
        recv_size= len(recv_data)
    print("命令的执行结果是",recv_data.decode("gbk"))
tcp_client.close

解决粘包client端

(4)TCP实现并发

基于tcp的套接字,关键就是两个循环,一个链接循环,一个通信循环,利用socketserver模块中的两大类:server类(解决链接问题)和request类(解决通信问题)来实现并发

import socketserver
class MyServer(socketserver.BaseRequestHandler):
    def handle(self):
        print("conn in:",self.request)#conn
        print("addr is:",self.client_address)#addr

        while True:
            #收消息
            try:
                data = self.request.recv(1024)
                if not data:break
                print("客户端收到的消息是:",data)

                #发消息
                self.request.sendall(data.upper())
            except Exception:
                break
if __name__ == "__main__":
    s = socketserver.ThreadingTCPServer(("10.10.27.37",8181),MyServer)#多进程
    # s = socketserver.ForkingTCPServer(("10.10.27.37",8181),MyServer)#多线程,系统开销高于多进程

    s.serve_forever()

server端

from socket import *
ip_port = ("10.10.27.37",8181)
back_log = 5
buffer_size = 1024

tcp_client = socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port)

while True:
    msg = input(">>>").strip()
    if not msg:continue

    tcp_client.send(msg.encode("utf-8"))
    data = tcp_client.recv(1024)
    print("收到服务端发来的消息",data.decode("utf-8"))

tcp_client.close

client端

原文地址:https://www.cnblogs.com/hechengwei/p/9141527.html

时间: 2024-10-11 07:03:34

Python开发——12.socket编程的相关文章

Linux统系统开发12 Socket API编程3 TCP状态转换 多路IO高并发select poll epoll udp组播 线程池

[本文谢绝转载原文来自http://990487026.blog.51cto.com] Linux统系统开发12 Socket API编程3 TCP状态转换 多路IO高并发select  poll  epoll udp组播 线程池 TCP 11种状态理解: 1,客户端正常发起关闭请求 2,客户端与服务端同时发起关闭请求 3,FIN_WAIT1直接转变TIME_WAIT 4,客户端接收来自服务器的关闭连接请求 多路IO转接服务器: select模型 poll模型 epoll模型 udp组播模型 线

python基础之socket编程

python基础之socket编程   一 TCP/IP五层模型 在每一层都工作着不同的设备,比如我们常用的交换机就工作在数据链路层的,一般的路由器是工作在网络层的. 在每一层实现的协议也各不同,即每一层的服务也不同.下图列出了每层主要的协议. 各层功能 注明:ARP和RAPR两个到底属于哪一层呢? 由于IP协议使用了ARP协议,所以经常把ARP协议划到网络层,但是ARP协议是为了从网络层使用的IP地址解析出在数据链路层使用的MAC地址,所以有些地方也把ARP协议划分到数据链路层,但是一般情况下

Python学习记录-socket编程

Python学习记录-socket编程 学习 python socket Python学习记录-socket编程 1. OSI七层模型详解 2. Python socket 3. socket()函数 4. TCP socket通信流程 5. Python Internet 模块 1. OSI七层模型详解 以上图见:http://blog.csdn.net/yaopeng_2005/article/details/7064869 其它详情可参考:socket网络基础 2. Python sock

Python 基础之socket编程(二)

Python 基础之socket编程(二) 昨天只是对socket编程做了简单的介绍,只是把socket通信的框架搭建起来,要对其中的功能进行进一步的扩充,就来看看今天的料哈! 一.基于tcp的套接字 1. tcp的服务端 ss = socket() #创建服务器套接字 ss.bind() #把地址绑定到套接字 ss.listen() #监听链接 inf_loop: #服务器无限循环 cs = ss.accept() #接受客户端链接 comm_loop: #通讯循环 cs.recv()/cs.

Python 基础之socket编程(三)

python 基础之socket编程(三) 前面实现的基于socket通信只能实现什么呢?在tcp协议的通信中就是一个用户说一句,服务端给你回一句,你再给服务端说一句,服务端再给你回一句,就这样一直友好的玩耍下去了.等等,又有一个用户来了,他呢也想和和服务端进行一下交流,于是他就给服务端发送了一条消息,之后等呀等不知过了多久,任然没有等到服务端给他发挥的消息,只有什么时候他就可以和服务端愉快的玩耍了呢?这个就需要第一个用户退出和服务器的链接,此时第二个客户端才会和服务端建立起链接,此时此刻,他才

Python基础12 - Socket网络编程

一.计算机网络 网络通信要素: 1.IP地址:A.用来标识网络上一台独立的主机 B.IP地址 = 网络地址 + 主机地址(网络号:用于识别主机所在的网络/网段.主机号:用于识别该网络中的主机) C.特殊的IP地址:127.0.0.1(本地回环地址.保留地址,点分十进制)可用于简单的测试网卡是否故障.表示本机. 2.端口号:A.用于标识进程的逻辑地址.不同的进程都有不同的端口标识. B.端口:要将数据发送到对方指定的应用程序上,为了标识这些应用程序,所以给这些网络应用程序都用数字进行标识.为了方便

python基础之socket编程 (转自林海峰老师)

阅读目录 一 客户端/服务器架构 二 osi七层 三 socket层 四 socket是什么 五 套接字发展史及分类 六 套接字工作流程 七 基于TCP的套接字 八 基于UDP的套接字 九 粘包现象 十 什么是粘包 十一 解决粘包的low比处理方法 十二 峰哥解决粘包的方法 十三 认证客户端的链接合法性 十四 socketserver实现并发 十五 作业 一 客户端/服务器架构 1.硬件C/S架构(打印机) 2.软件C/S架构 互联网中处处是C/S架构 如黄色网站是服务端,你的浏览器是客户端(B

python进阶---Python中的socket编程(一)

初识socket编程 一.前言 socket基于C\S架构(客户端\服务端)的编程模型,在Python中是以socket模块存在的. Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议. 所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规

Python下的socket编程

首先需要说明的一点是:这里并不会记录很深奥的socket编程,只是会分析一个最简单的socket编程聊天室下的几种特殊异常情况的处理,代码如下: 服务端: 1 import socket 2 3 HOST = "" 4 PORT = 8870 5 sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 6 sk.bind((HOST, PORT)) 7 sk.listen(5) 8 while True: 9 print("