1. 什么是socket?

  socket是套接字的英文名称, 我们知道在TCP/IP协议簇体系中,将网络状态分为了应用层、传输层、网络层、物理层等四种状态,而socket是与传输层密切相关的,其主要实现协议为TCP及UDP。传输层实现端到端的通信,因此,每一个传输层连接有两个端点。那么,传输层连接的端点是什么呢?不是主机,不是主机的IP地址,不是应用进程,也不是传输层的协议端口。传输层连接的端点叫做套接字(socket)。根据RFC793的定义:端口号拼接到IP地址就构成了套接字。所谓套接字,实际上是一个通信端点,每个套接字都有一个套接字序号,包括主机的IP地址与一个16位的主机端口号,即形如(主机IP地址:端口号)。例如,如果IP地址是210.37.145.1,而端口号是23,那么得到套接字就是(210.37.145.1:23)。
总之,套接字Socket=(IP地址:端口号),套接字的表示方法是点分十进制的IP地址后面写上端口号,中间用冒号或逗号隔开。每一个传输层连接唯一地被通信两端的两个端点(即两个套接字)所确定。
套接字可以看成是两个网络应用程序进行通信时,各自通信连接中的一个端点。通信时,其中的一个网络应用程序将要传输的一段信息写入它所在主机的Socket中,该Socket通过网络接口卡的传输介质将这段信息发送给另一台主机的Socket中,使这段信息能传送到其他程序中。因此,两个应用程序之间的数据传输要通过套接字来完成。
在网络应用程序设计时,由于TCP/IP的核心内容被封装在操作系统中,如果应用程序要使用TCP/IP,可以通过系统提供的TCP/IP的编程接口来实现。在Windows环境下,网络应用程序编程接口称作Windows Socket。为了支持用户开发面向应用的通信程序,大部分系统都提供了一组基于TCP或者UDP的应用程序编程接口(API),该接口通常以一组函数的形式出现,也称为套接字(Socket)。
 

1. socket编程基本流程图

     

2. socket、threading实现聊天通信 

# coding:utf-8
# 服务端: import socket
import threading HOST, PORT = "localhost",
address = (HOST, PORT) # socket.AF_INET代表IPV4, socket.AF_INET6代表IPV6
# socket.SOCK_STREAM代表TCP, SOCK_DGRAM代表UDP
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(address) server.listen() def handlesock(sock, addr):
while True:
data = sock.recv().decode("utf-8")
if data == "exit":
break
print("收到来自客户端[{0}]的消息, 内容是[{1}]".format(addr, data))
response = input("回复[{0}]:\t".format(addr)).encode("utf8")
sock.send(response) while True:
sock, addr = server.accept()
cur_sock = threading.Thread(target=handlesock, args=(sock, addr))
cur_sock.start()
# 客户端:
import socket client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("localhost", )) while True:
response = input("回复服务器:").encode("utf8")
client.send(response)
if response=="exit":
break
data = client.recv().decode("utf8")
print("收到来自服务器的消息:%s" % data) client.close()

  解释:对于单用户、单服务来说,一个服务器只服务一个用户对象, 那么要想实现两端通信, 能够通过循环来实现输入与输出;若是服务于多个用户对象, 就需要保留每一个连接(socket实例)对象来单独处理,这里我们采用了线程的方式,即每接收一个连接请求,就新开辟一个线程来处理(考虑到内存开销也可以用线程池来替代)。

3. socket模拟浏览器发送http请求

  在此之前,我们必须弄懂相关概念,

  ###:socket并不是一种协议,而是对TCP/IP协议(或UDP/IP协议)的高级封装,其与TCP连接是可以存在包含关系的,换一句话说,socket就是一种通信接口,拥有实现可靠连接的功能。此外,当socket(网络套接字)采用TCP来建立连接的时候,是面向长连接的。

  ###:http连接显著的特点是客户端发送的每次请求都需要服务器回送相应响应,在请求结束后,会主动释放连接,从建立连接到关闭连接的过程称之为“一次连接”。此外HTTP连接是无状态的,所以在请求过程中需要发送请求头来标明请求身份。

  (HTTP请求底层是一种特殊处理后的socket,是面向短连接的)

#coding:utf-8

from urllib.parse import urlparse
import socket class SocketHttp():
"""模拟http请求"""
def __init__(self, url: str):
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.url = url def parse_url(self):
"""url解析"""
self.url = urlparse(self.url)
domain = self.url.netloc
path = self.url.path
if path == "":
path = "/"
return domain, path def request(self):
"""建立socket连接, 请求url"""
domain, path = self.parse_url()
self.client.connect((domain, 80))
request_data = """GET {0} HTTP/1.1\r\nHost: {1}\r\nConnection: close\r\n\r\n""".format(path, domain).encode("utf8")
self.client.send(request_data)
# 读取数据
response = b""
while True:
cur_data = self.client.recv(1024)
if cur_data:
response+=cur_data
else:
break
self.client.close()
return response.decode() if __name__ == '__main__':
client_http = SocketHttp("https://www.baidu.com/")
print(client_http.request())

  输出:

  

  我们也可以采用异步非阻塞IO来获取http响应

#coding:utf-8

from urllib.parse import urlparse
import socket class SocketHttp():
"""模拟http请求"""
def __init__(self, url: str):
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.client.setblocking(False)
self.url = url def parse_url(self):
"""url解析"""
self.url = urlparse(self.url)
domain = self.url.netloc
path = self.url.path
if path == "":
path = "/"
return domain, path def request(self):
"""建立socket连接, 请求url"""
domain, path = self.parse_url()
try:
self.client.connect((domain, 80))
except BlockingIOError:
pass
request_data = """GET {0} HTTP/1.1\r\nHost: {1}\r\nConnection: close\r\n\r\n""".format(path, domain).encode("utf8") # 轮询尝试发送,直到与服务器连接成功
while 1:
try:
self.client.send(request_data)
except OSError as e:
pass
else:
break # 读取数据
response = b""
while True: # 不断轮询接收数据
while 1:
try:
cur_data = self.client.recv(1024)
except BlockingIOError as e:
pass
else:
break if cur_data:
response+=cur_data
else:
break
self.client.close()
return response.decode() if __name__ == '__main__':
client_http = SocketHttp("https://www.baidu.com/")
print(client_http.request())

异步非阻塞IO

4.非租塞IO

  当两个通信实体成功建立连接之后,recv方法调用之后,其内核空间与应用空间的发生的数据复制行为如下

  

  可以看出,若调用recv方法之后,数据未准备好, socket所处线程一直处于阻塞状态,直至数据复制到用户空间。这时,CPU未得到有效利用。那么如何才能利用起cpu资源呢?轮询方式便产生了(非阻塞I/O模式)

  当线程执行recv方法时,不再让该线程睡眠,而是立即返回一个错误状态,以此不断轮询直到返回正确状态才退出循环,值得注意的是该轮询的方式需要我们手动实现。如下:

  

  这里我们将客户端程序设置为非阻塞I/O模式,服务器程序不作修改,结果出现运行错误:

  

  

  此处出错误是合理的,因为在非阻塞I/O模式下,模式发生改变,调用connnect方法便出现异常,此外,recv方法在调用时数据可能未准备好,所以这里需要手动实现轮询,如下:

import socket
import threading HOST, PORT = "localhost", 8020
address = (HOST, PORT) server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(address)
server.listen()
print("server is listening...")
def handlesock(sock, addr):
while True:
data = sock.recv(1024).decode("utf-8")
if data == "exit":
break
print("收到来自客户端[{0}]的消息, 内容是[{1}]".format(addr, data))
response = input("回复[{0}]:\t".format(addr)).encode("utf8")
sock.send(response) while True:
sock, addr = server.accept()
cur_sock = threading.Thread(target=handlesock, args=(sock, addr))
cur_sock.start()

服务端-阻塞式I/O

import socket

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.setblocking(0) try:
client.connect(("localhost", 8020))
except BlockingIOError:
pass while True:
response = input("回复服务器:").encode("utf8")
client.send(response)
if response=="exit":
break # 非阻塞I/O轮询方式
while True:
try:
data = client.recv(1024)
except BlockingIOError as e:
pass
else:
if data:
data = data.decode("utf8")
break print("收到来自服务器的消息:%s" % data) client.close()

客户端-非阻塞式I/O

  

  

  

浅析python-socket编程的更多相关文章

  1. Python Socket 编程——聊天室示例程序

    上一篇 我们学习了简单的 Python TCP Socket 编程,通过分别写服务端和客户端的代码了解基本的 Python Socket 编程模型.本文再通过一个例子来加强一下对 Socket 编程的 ...

  2. python/socket编程之粘包

    python/socket编程之粘包 粘包 只有TCP有粘包现象,UDP永远不会粘包. 首先需要掌握一个socket收发消息的原理 发送端可以是1k,1k的发送数据而接受端的应用程序可以2k,2k的提 ...

  3. PYTHON SOCKET编程简介

    原文地址: PYTHON SOCKET编程详细介绍   Python 提供了两个基本的 socket 模块. 第一个是 Socket,它提供了标准的 BSD Sockets API. 第二个是 Soc ...

  4. python socket编程笔记

    用python实现一个简单的socket网络聊天通讯 (Linux --py2.7平台与windows--py3.6平台) 人生苦短之我用Python篇(socket编程) python之路 sock ...

  5. [Python_7] Python Socket 编程

    0. 说明 Python Socket 编程 1. TCP 协议 [TCP Server] 通过 netstat -ano 查看端口是否开启 # -*-coding:utf-8-*- "&q ...

  6. Python Socket 编程示例 Echo Server

    简评:我们已经从「Python Socket 编程概览」了解了 socket API 的概述以及客户端和服务器的通信方式,接下来让我们创建第一个客户端和服务器,我们将从一个简单的实现开始,服务器将简单 ...

  7. Python Socket 编程——聊天室演示样例程序

    上一篇 我们学习了简单的 Python TCP Socket 编程,通过分别写服务端和client的代码了解主要的 Python Socket 编程模型.本文再通过一个样例来加强一下对 Socket ...

  8. python socket编程入门(编写server实例)+send 与sendall的区别与使用方法

    python 编写server的步骤: 1. 第一步是创建socket对象.调用socket构造函数.如: socket = socket.socket( family, type ) family参 ...

  9. 第九章:Python高级编程-Python socket编程

    第九章:Python高级编程-Python socket编程 Python3高级核心技术97讲 笔记 9.1 弄懂HTTP.Socket.TCP这几个概念 Socket为我们封装好了协议 9.2 cl ...

  10. python socket编程详细介绍

    Python 提供了两个基本的 socket 模块. 第一个是 Socket,它提供了标准的 BSD Sockets API. 第二个是 SocketServer, 它提供了服务器中心类,可以简化网络 ...

随机推荐

  1. 【洛谷 P3674】 小清新人渣的本愿(bitset,莫队)

    题目链接 因为每个数都是\(10^5\)以内,考虑直接用\(bitset\)维护. \(a-b=x\),其实就是看是否有\(p\)和\(p+x\)同时存在,直接\(bitset\)移位按位与一下就好了 ...

  2. 深入理解JVM(四) -- 垃圾内存回收的判定方法和内容

    上一篇文章我们学到了对象在内存中是如何存储的已经是如何被访问的,这篇文章将介绍当内存空间不够时,虚拟机将怎样判定对象可不可以被回收已经哪些地方会发生回收. 垃圾回收主要(不是全部)发生在堆内存中,当一 ...

  3. 【转载】关于SimpleDateFormat安全的时间格式化线程安全问题

    想必大家对SimpleDateFormat并不陌生.SimpleDateFormat 是 Java 中一个非常常用的类,该类用来对日期字符串进行解析和格式化输出,但如果使用不小心会导致非常微妙和难以调 ...

  4. python day 8: re模块补充,导入模块,hashlib模块,字符串格式化,模块知识拾遗,requests模块初识

    目录 python day 8 1. re模块补充 2. import模块导入 3. os模块 4. hashlib模块 5. 字符串格式:百分号法与format方法 6. 模块知识拾遗 7. req ...

  5. springboot整合ActiveMQ1(基本使用)

    基本使用,https://www.tapme.top/blog/detail/2018-09-05-10-38 主备模式,https://www.tapme.top/blog/detail/2018- ...

  6. SQL SERVER-修改实例的排序规则

    系统库是无法直接修改的,只能修改用户数据库排序规则(要先解决依赖项.如表函数): ALTER DATABASE [xx] COLLATE Chinese_PRC_CI_AS 修改实例的排序规则,使用安 ...

  7. php初识2

    php概述 什么是php,PHP语言的优势,PHP5的新特性,PHP的发展趋势,PHP的应用领域. PHP是超文本预处理器,是一种服务器端,跨平台,HTML嵌入式的脚本语言,具有c语言,Java语言, ...

  8. 如何解决redis的并发竞争问题?

    这个也是线上非常常见的一个问题,就是多客户端同时并发写一个key,可能本来应该先到的数据后到了,导致数据版本错了.或者是多客户端同时获取一个key,修改值之后再写回去,只要顺序错了,数据就错了. 而且 ...

  9. python_面向对象——动态创建类和isinstance和issubclass方法

    # 给动态生产的类定义一个方法 def __init__(self,name): self.name = name print(self.name) def take(self,obj): print ...

  10. SVM: 用kernels(核函数)来定义新的features,避免使用多项式,高斯kernel

    应用kernels来进行非线性分类 非线性分类:是否存在好的features的选择(而不是多项式)--f1,f2,f3.... 上图是一个非线性分类的问题,前面讲过,我们可以应用多项式(feature ...