socket 编程

Posted Cloud-Tony

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了socket 编程相关的知识,希望对你有一定的参考价值。

C/S架构与socket 的关系:

学习socket 编程就是为完成C/S 架构的开发

image_thumb10

socket 编程 与 互联网协议

  1. 学习socket的目的:目标就是掌握socket编程,开发自己的C/S架构软件
  2. C/S架构的软件,(软件属于应用层)是基于网络进行通信的
  3. 网络的核心就是一堆协议,协议就是标准,要开发一款基于网络通信的软件,就必须遵循这些标准

image_thumb1[1]

TCP/IP 协议族包括运输层、网络层、链路层、

socket 层:在哪???看下图

所有的互联网协议都被 socket 包涵,或者说都被socket 这一个囊括了,而socket 处于 应用层 与 运输层 之间,

image_thumb41

那既然 socket 已经将整个互联网的协议包括在它里面了, 就不用再去研究什么 TCP/UDP这些协议了,因为socket已经为我们封装好了

,所以我们写应用程序 时 只要遵循 socket的规定,写出的程序就遵循TCP/UDP的标准了

基于文件类型的套接字家族: AF_UNIX (address and protocol familes)

基于网络类型的套接字家族:AF_INET

AF_INET6被用于ipv6,还有一些其它的地址家庭,AF_INET是使用最广泛的一个。

image_thumb101

套接字工作流程:

打电话场景: 你有一个手机,开机,上卡,要打电话给一个朋友,先拨号,朋友听到电话铃声后拿起电话(看电话号码)并接电话,这个时候就已经建立起了连接,

,通了以后进行通话,通话结束后挂断电话,此次通话完成。

image_thumb12

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)

服务端 建立连接 步骤

三种最流行的套接字类型是:stream ,datagram 和 raw套接字可以直接与TCP协议进行接口,而 raw套接字则接口到IP协议。但套接字并不限于TCP/IP
#套接字模块
#使用该模块可以实现客户机与服务器套接字,要在PYTHON中建立具有TCP和流套接字的简单服务器,需要使用socket模块,利用该模块包含的和类定义,可生成通过网络通信的程序,一般来说,建立服务器连接需要六个步骤。

#第一步:创建socket对象,调用socket 构造函数
import socket
#socket=socket.socket(family,type)

QQ_talk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

#第二步则是将 socket 绑定(指派)到指定地址上,socket.bind(address,port)
QQ_talk.bind((\'192.168.100.149\',9000))
#address 是一个双元素元组((host,port)),主机名或者ip地址+端口号,如果端口号正在被使用或者保留,或者主机名IP地址错误,则引发socket.error异常。

#第三步:绑定后,必须准备好套接字,以便接受连接请求
QQ_talk.listen(2)

#socket.listen(backlog)
#backlog 指定了最多连接数,至少为1,接到连接请求后,这些请求必须排队,如果队列已满,则拒绝请求

#第四步:服务器套接字通过 socket的accept方法等待客户请求一个连接:
conn,addr=QQ_talk.accept()
#调用accept 方法时,socket会进入‘waiting’(或阻塞)状态,客户请求连接时,方法建立连接并返回服务器accept 方法 返回一个含有两个元素的元组,形如(conn,addr),第一个元素conn是新的socket对象,服务器通过它与客户通信,第二个元素(addr)是客户的internet地址
#等于 conn 是一个新对象new object包括地址和端口,  addr是客户的IP地址

#第五步是处理阶段,服务器和客户通过send和recv方法通信(传输数据)。服务器调用send,并采用字符串形式向客户发送信息,send方法返回已发送的字符上数,服务器使用 recv方法从客户接受信息,调用recv 时,必须指定一个整数亚控制本次调用所接受的最大数据量。 recv方法在接受数据时会进入\'blocket\'  状态,最后返回一个字符串,用它来表示收到的数据,如果发送的量超过recv所允许,数据会被截断,多余的数据将缓冲于接受以后调用recv时,多余的数据会从缓冲区删除。
client_data=conn.recv(1024)
conn.send(\'copy\')

#第六步:传输结束,服务器调用socket的close方法以关闭连接
QQ_talk.close()

客户端 建立连接 需要4个步骤

#简单客户连接则需要4个步骤:
#第一步: 创建一个socket对象以连接服务器  socket=socket.socket(famaily,type)
import socket
QQ_talk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

#第二步:使用socket的 connect方法连接服务器socket.conect((host,port))
conn,addr=QQ_talk.connect((\'192.168.100.149\',9090))

#第三步:客户和服务器通过send和recv方法通信。
server_data=QQ_talk.recv()
server_=QQ_talk.send(\'client copy\')

#第四步:结束后,客户通过调用socket 的 close 方法来关闭连接
QQ_talk.close()

练习:

使用socket 完成远程执行linux端的命令

#!/usr/bin/env python
#!-*- coding:utf-8 -*-
import socket
session=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# phone.connect((\'127.0.0.1\',8090)) #这个操作对应server的 accept
session.connect((\'192.168.100.149\',9000)) #这个操作对应server的 accept

while True:
    session.send(\'hostname \'.encode(\'utf-8\'))
    hostname=session.recv(1024)
    session.send(\'who am i\'.encode(\'utf-8\'))
    username=session.recv(1024)
    # print(username.decode(\'utf-8\').split()[0])
    msg=input(\'%s@%s# \'%(username.decode(\'utf-8\').split()[0],hostname.decode(\'utf-8\').strip())).strip()
    if not msg:
        continue
    session.send(msg.encode(\'utf-8\')) #这个操作对应server 的recv
    data=session.recv(1024)  #对应server的send
    print(data.decode(\'utf-8\'))   #打印server send过来的 信息
session.close()

server 端

#!/usr/bin/env python
#!-*- coding:utf-8 -*-
import socket,subprocess,os
session=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

#phone.bind((\'192.168.100.149\',8080))
session.bind((\'192.168.100.149\',9000))

session.listen(5)
while True:
    conn,addr=session.accept()
#    print(\'client  is \',conn)
    print(\'========Host:  \',addr)
    while True:
        try:
             data=conn.recv(1024)
             if not data:break
             print(\'client send info: \',data)
            # if len(data)>=2:
            #    os.system(data)
            # else:
            # data=\'\'.join(data)
             res=subprocess.Popen(data,shell=True,stdout=subprocess.PIPE)
             res_cmd=res.stdout.read()
             #if not res_cmd:
                 #os.system(data)
                 
             conn.send(res_cmd)
        except Exception:
            break

    conn.close()
sess.close()

客户端 :

image_thumb11

服务端:

image_thumb4

socketserver 实现并发

image_thumb3

介绍:

socketserver 实现并发
在之前学习中,服务端的第一个特点是:
1、一直运行提供服务(链接循环)基于一个连接的通信循环
            2、要解决的就是连接循环和通信循环
这两个要求在之前的程序中都没有实现。现在需要通过socketserver实现并发
 
 
为什么使用:

在访问百度的时候,百度的服务器不只服务于你一个用户的吧, 百度服务器同时在服务N个 多个用户的访问

 
服务端使用:
import socketserver
class Ftpserver(socketserver.BaseRequestHandler): # 要用就是这种形式,必须这样写, 这里解决的是通信
    def handle(self):
        print(self)
        print(self.request)  # 拿到的是conn
        while True:
            data=self.request.recv(1024)
            print(data)
            self.request.send(data.upper())

if if __name__ == \'__main__\':
    obj=socketserver.ThreadingTCPServer((\'192.168.100.149\',9000),Ftpserver) #线程
    obj.serve_forever()  #连接循环  等于多份accept()
 

如何用:

多个客户端访问服务端 ,已经实现并发

image_thumb7

完整代码剖析:

image_thumb13

基于udp 套接字: UDP是无连接

server 端

import socket
udpserver=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
udpserver.bind((\'192.168.100.149\',9000))

while True:
    data=udpserver.recvfrom(1024)
    print(data)

client 端

import socket
udpclient=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# udpclient.connect((\'192.168.100.149\',9000))
udpserver=(\'192.168.100.149\',9000)
while True:
    inp=input(\'>>>>: \').strip()
    udpclient.sendto(inp.encode(\'utf-8\'),udpserver)

换种写法来实现:

server 端

import socket
class recvdata:
    def __init__(self,pro_ipaddr,pro_port):
        self.pro_ipaddr=pro_ipaddr
        self.pro_port=pro_port
    def process(self):
        while True:
            auth_obj = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            auth_obj.bind((self.pro_ipaddr,self.pro_port))
            client_data,client_addr=auth_obj.recvfrom(1024)
            print(client_data.decode(\'utf-8\').split(),client_addr)
            auth_obj.sendto(client_data,client_addr)

server=recvdata(\'127.0.0.1\',9000)
server.process()

client端:

import socket
auth=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
authserver=(\'127.0.0.1\',9000)
while True:
    msg=input(\'>>>>>: \')
    auth.sendto(msg.encode(\'utf-8\'),authserver)
    msg,addr = auth.recvfrom(1024)
    print(msg.decode(\'utf-8\').split())

效果:

服务端

image_thumb1

多个客户端:

image_thumb3[1]

image_thumb5

以上是关于socket 编程的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段——JS中的面向对象编程

VSCode自定义代码片段9——JS中的面向对象编程

使用 Pygments 检测代码片段的编程语言

面向面试编程代码片段之GC

如何在 Django Summernote 中显示编程片段的代码块?

以编程方式将按钮添加到片段