循序渐进Python3(八) -- 初识socket

socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。
socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)

socket和file的区别:

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

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


scoket 实现流程:

小例子:

服务端:

#!/usr/bin/env python# Version = 3.5.2# __auth__ = ‘无名小妖‘import socket

ip_port = (‘127.0.0.1‘,9999)

sk = socket.socket()sk.bind(ip_port)sk.listen(5)

while True:    print(‘server waiting...‘)    conn, addr = sk.accept()    client_data = conn.recv(1024)    # print(conn,addr)    print(str(client_data,encoding=‘utf8‘))    conn.sendall(bytes(‘不要回答,不要回答,不要回答‘,encoding=‘utf8‘))    conn.close()

客户端:
#!/usr/bin/env python# Version = 3.5.2# __auth__ = ‘无名小妖‘import socketip_port = (‘127.0.0.1‘,9999)

sk = socket.socket()sk.connect(ip_port)sk.sendall(bytes(‘请求占领地球‘,encoding=‘utf8‘))server_reply = sk.recv(1024)print(str(server_reply,encoding=‘utf8‘))sk.close()

更多功能

sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0) 参数一:地址簇    socket.AF_INET IPv4(默认)   socket.AF_INET6 IPv6    socket.AF_UNIX 只能够用于单一的Unix系统进程间通信 参数二:类型    socket.SOCK_STREAM  流式socket , for TCP (默认)   socket.SOCK_DGRAM   数据报式socket , for UDP    socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。   socket.SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。   socket.SOCK_SEQPACKET 可靠的连续数据包服务 参数三:协议    0  (默认)与特定的地址家族相关的协议,如果是 0 ,则系统就会根据地址格式和套接类别,自动选择一个合适的协议 详细介绍: sk.bind(address)  sk.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。sk.listen(backlog)  开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。 backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5。 这个值不能无限大,因为要在内核中维护连接队列sk.setblocking(bool)  是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。sk.accept()  接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。  接收TCP 客户的连接(阻塞式)等待连接的到来sk.connect(address)  连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。sk.connect_ex(address)  同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061sk.close()  关闭套接字sk.recv(bufsize[,flag])  接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。sk.recvfrom(bufsize[.flag])  与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。sk.send(bytes[,flag])  将bytes中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于bytes的字节大小。即:可能未将指定内容全部发送。 sk.sendall(bytes[,flag])  将bytes中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。 内部通过递归调用send,将所有内容发送出去。sk.sendto(bytes[,flag],address)   将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。sk.settimeout(timeout)  设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )sk.getpeername()  返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。sk.getsockname()  返回套接字自己的地址。通常是一个元组(ipaddr,port)sk.fileno()  套接字的文件描述符

了解了基本用法后,我们在看看有那些需要注意的:

    1.基于python3.5.2版本的socket只能收发字节(python2.7可以发送字符串);     2.客户端退出不能影响服务端;     3.sk.accept()和sk.recv()是阻塞的(连接正常的情况下);      客户端退出不能影响服务端的列子: 服务端代码:
import socket, subprocessip_port = (‘127.0.0.1‘, 9995)server = socket.socket()server.bind(ip_port)server.listen(3)# 此while循环为了持续的接收连接(当一个连接断开,接收另一个)while True:    conn, addr = server.accept()    # 此while循环为了持续收发消息    while True:        try:            recv_data = conn.recv(1024)            print(recv_data,‘xxx‘)            if recv_data == bytes(‘exit‘,encoding=‘utf8‘):                break            elif len(recv_data) == 0: # 当客户端发送空后,服务端退出本次连接                break            send_data = recv_data.upper()            conn.send(send_data)        except Exception as e:            print(e)            break    conn.close()
客户端代码:
import socketip_port = (‘127.0.0.1‘, 9995)# 买手机client = socket.socket()# 拨号client.connect(ip_port)while True:    # 发消息    send_data = input(‘>>>‘).strip()    client.send(bytes(send_data, encoding=‘utf8‘))    if send_data == ‘exit‘:        break    elif send_data == ‘‘:        continue    # 收消息    recv_data = client.recv(1024)    print(‘>>>{}‘.format(str(recv_data, encoding=‘utf8‘)))

-----------------------------------------------------------------------------------------------

再来一个高级点的列子,用socket实现ssh功能:

服务端代码:
import socket, subprocessip_port = (‘127.0.0.1‘, 9999)server = socket.socket()server.bind(ip_port)server.listen(3)# 此while循环为了持续的接收连接(当一个连接断开,接收另一个)while True:    conn, addr = server.accept()    # 此while循环为了持续收发消息    while True:        try:            recv_data = conn.recv(1024)            print(recv_data,‘xxx‘)            if recv_data == bytes(‘exit‘,encoding=‘utf8‘):                break            elif len(recv_data) == 0: # 当客户端发送空后,服务端退出本次连接                break            p = subprocess.Popen(str(recv_data,encoding=‘utf8‘),shell=True,stdout=subprocess.PIPE)            res = p.stdout.read()            if len(res) == 0: # 客户端输入错误命令时,服务端返回空                  send_data = ‘cmd error‘
            else:
           # 由于windows是gbk编码,所以需要进行解码(gbk无法直接转化为utf8,必须先转为字符串)                send_data = str(res,encoding=‘gbk‘)             conn.send(bytes(send_data,encoding=‘utf8‘))        except Exception as e:            print(e)            break    conn.close()

客户端代码:
import socketip_port = (‘127.0.0.1‘, 9999)# 买手机client = socket.socket()# 拨号client.connect(ip_port)while True:    # 发消息    send_data = input(‘>>>‘).strip()    client.send(bytes(send_data, encoding=‘utf8‘))    if send_data == ‘exit‘:        break    elif send_data == ‘‘:        continue    # 收消息    recv_data = client.recv(1024)    print(‘>>>{}‘.format(str(recv_data, encoding=‘utf8‘)))
------------------------------------------------------------------------------------------

粘包问题

当服务端要反馈的数据大于客户端一次能接收的大小时,服务端的剩余数据就会“残留”在服务器端,下次反馈数据时会先把残存的数据先发送到客户端,造成请求的数据和反馈的数据不相对应,这种现象就是 粘包。
解决粘包的方法很简单,就是服务器端提前告诉客户端要发送多大的数据,然后客户端循环接收,直到接收完毕,退出循环。
代码实现:
服务器端:
#!/usr/bin/env python# Version = 3.5.2# __auth__ = ‘无名小妖‘

import socket, subprocessip_port = (‘127.0.0.1‘, 9999)server = socket.socket()server.bind(ip_port)server.listen(3)# 此while循环为了持续的接收连接(当一个连接断开,接收另一个)while True:    conn, addr = server.accept()    # 此while循环为了持续收发消息    while True:        try:  # 客户端异常关闭处理            recv_data = conn.recv(1024)            print(recv_data,‘xxx‘)            if recv_data == bytes(‘exit‘, encoding=‘utf8‘):                break            elif len(recv_data) == 0:  # 当客户端发送空后,服务端退出本次连接                break            p = subprocess.Popen(str(recv_data,encoding=‘utf8‘),shell=True,stdout=subprocess.PIPE)            res = p.stdout.read()            if len(res) == 0:  # 客户端输入错误命令时,服务端返回空                send_data = ‘cmd error‘            else:                # 将gbk转为utf8,需要先转为str作为过度                send_data = str(res, encoding=‘gbk‘)                send_data = bytes(send_data, encoding=‘utf8‘)            # 发送一个说明,告诉客户端要发送多大的包(解决粘包问题)            ready_flag = ‘Ready|{}‘.format(len(send_data))            conn.send(bytes(ready_flag, encoding=‘utf8‘))            feed_back = conn.recv(1024)            feed_back = str(feed_back,encoding=‘utf8‘)            if feed_back == ‘Start‘:                conn.send(send_data)                print(str(send_data, encoding=‘utf8‘))        except Exception as e:            print(e)            break    conn.close()
客户端:
#!/usr/bin/env python# Version = 3.5.2# __auth__ = ‘无名小妖‘import socketip_port = (‘127.0.0.1‘, 9999)# 买手机client = socket.socket()# 拨号client.connect(ip_port)while True:    # 发消息    send_data = input(‘>>>‘).strip()    client.send(bytes(send_data, encoding=‘utf8‘))    if send_data == ‘exit‘:        break    elif send_data == ‘‘:        continue    # 处理服务端发送过来的说明,明确即将收多大包(解决粘包问题)    ready_tag = client.recv(1024)    ready_tag = str(ready_tag, encoding=‘utf8‘)    if ready_tag.startswith(‘Ready‘):        msg_size = int(ready_tag.split(‘|‘)[-1])    start_tag = ‘Start‘    client.send(bytes(start_tag,encoding=‘utf8‘))

recv_size = 0    recv_msg = b‘‘    while recv_size < msg_size:        recv_data = client.recv(1024)        recv_msg += recv_data        recv_size += len(recv_data)        print(recv_size,msg_size)    # 收消息    print(‘>>>{}‘.format(str(recv_msg, encoding=‘utf8‘)))
------------------------------------------------------------------------------------------

SocketServer模块


SocketServer内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。

即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求。
下面看一个简单的socketserver的例子:
服务器端:
import socketserver

class SocketServer(socketserver.BaseRequestHandler):    def handle(self):        self.request.sendall(bytes(‘Welcome to my socketserver!‘,encoding=‘utf8‘))        while True:            recv_data = self.request.recv(1024)            self.request.sendall(recv_data.upper())

if __name__ == ‘__main__‘:    server = socketserver.ThreadingTCPServer((‘127.0.0.1‘,8889),SocketServer)    server.serve_forever()

客户端:
import socket

ip_port = (‘127.0.0.1‘,8889)sk = socket.socket()sk.connect(ip_port)

WELCOME_WORDS = sk.recv(1024)print(WELCOME_WORDS.decode())

while True:    send_data = input(‘>>>‘).strip()    sk.send(bytes(send_data, encoding=‘utf8‘))    recv_data = sk.recv(1024)    print(recv_data.decode())sk.close()

来自为知笔记(Wiz)

时间: 2024-08-18 11:37:15

循序渐进Python3(八) -- 初识socket的相关文章

二、网编之初识Socket套接字结构体

二.初识Socket套接字结构体 1.通用套接字结构体类型 struct sockaddr { sa_family_t sa_family; //协议簇 char sa_data[14]; //协议簇数据 } 通用套接字结构体可以在不同的协议簇之间进行强制转化,Socket网络编程中几乎所有套接字API函数的形参都是通用套接字结构体struct sockaddr. 存在问题: 通用套接字结构体对编程的角度来说,设置很不方便,我们以以太网协议来说,当要设置端口号.IP地址等,那么我需要将端口号与I

循序渐进Python3(三) -- 0 -- 初识函数

函数 如果我们要计算一个圆的面积,就需要知道它的半径,然后根据公式S=3.14*r*r算出它的面积,如果我们要算100个圆的面积,则每次我们都需要写公式去计算,是不是很麻烦,但是有了函数的话,我们就不再每次写S=3.14 *r*r, 而是把计算圆面积的功能写到一个函数里比如说s=areacircle(r),然后每次求面积的时候,只要把半径传递给函数就可以实现计算圆面积,这样我 们写代码就简单多了.这就是函数的功能.在编程中,函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段,能提高

循序渐进Python3(四) -- 初识模块

什么是模块? 模块,用一组代码实现了某个功能的代码集合. 类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合.而对于一个复杂的功能来,可能需要多个函数才能完成(函数又可以在不同的.py文件中),n个 .py 文件组成的代码集合就称为模块. 如:os 是系统相关的模块:file是文件操作相关的模块 import module #导入模块下的全部模块 from module.xx.xx import * #导入模块下的全部模块 from

Python3的tcp socket接收不定长数据包接收到的数据不全。

Python Socket API参考出处:http://blog.csdn.net/xiangpingli/article/details/47706707 使用socket.recv(pack_length)接收不定长的数据,如果数据包长度超过一定值,则接收的数据不全. 参照python3.4的文档可发现: socket.recv(bufsize[, flags]) Receive data from the socket. The return value is a bytes objec

Python3.4 threading socket 学习

#!/usr/bin/env python3 #file name: threadtest.py # -*- coding:utf8 -*- # -version:1.0- import threading, time, socket class Server():     '''接收消息的服务器类'''     def __init__(self, port):         self.port = port         self.server = socket.socket(socke

1、初识socket

经过近一个半月的学习我们已经度过了python基础的阶段,今天我们开始学习python网络编程,没有难以理解的逻辑,更注重的是记忆. 对网络协议和基础没有概念的可以在阅读本文前预习计算机基础3.网络协议:http://www.cnblogs.com/liluning/p/7170799.html 一.客户端/服务器架构 1.C/S结构,即Client/Server(客户端/服务器)结构 2.我们在互联网中处处可见c/s架构比如说浏览器,qq,lol,视频软件... 3.我们学习socket就是为

初识Socket通信:基于TCP和UDP协议学习网络编程

学习笔记: 1.基于TCP协议的Socket网络编程: (1)Socket类构造方法:在客户端和服务器端建立连接 Socket s = new Socket(hostName,port);以主机名和端口号作为参数来创建一个Socket对象. Socket s = new Socket(address,port);以InetAddress对象和端口号作为参数来创建一个Socket对象. 创建Socket对象时可能抛出UnknownHostException或IOException异常,必须捕获它们

初识socket

socket也叫套接字,用于通信的一个句柄,描述IP与端口信息,程序通过套接字可以向网络发出请求或者应答网络请求. socket起源与unix,而unix/Linux基本哲学之一就是”一切皆文件“,对于文件用[打开],[关闭],[读写]模式来操作, socket是该模式的一个实现,socket是一种特殊的文件,一切socket函数就是对其进行的操作(读/写IO,打开,关闭) file模块是针对某个指定文件进行打开,读写,关闭操作 socket模块是针对 服务端与客户端socket 进行打开,读写

循序渐进Python3(四) -- 装饰器、迭代器和生成器

初识装饰器(decorator ) Python的 decorator 本质上就是一个高阶函数,它接收一个函数作为参数,然后,返回一个新函数. 使用 decorator 用Python提供的 @ 语法,这样可以避免手动编写 f = decorate(f) 这样的代码. 先看一个例子: #!/usr/bin/env python def outer(func): print('这是outer.') def inner(): print('这是inner.') func() print('这是最后.