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. 1.工厂模式(Factory Method)

    注:图片来源于 https://www.cnblogs.com/-saligia-/p/10216752.html 工厂UML图解析: 工厂模式:client用户需要三步: 1.创建工厂: 2.生产产 ...

  2. PHP 中使用ajax时一些常见错误总结整理

    这篇文章主要介绍了PHP 中使用ajax时一些常见错误总结整理的相关资料,需要的朋友可以参考下 PHP作为后端时,前端js使用ajax技术进行相互信息传送时,经常会出错误,对于新手来说有些手足无措.总 ...

  3. 常见SVN图标的含义

    转自:https://www.cnblogs.com/genhaosan/articles/5129791.html 灰色向右箭头:本地修改过 蓝色向左箭头:SVN上修改过 灰色向右且中间有个加号的箭 ...

  4. Android目前流行三方数据库ORM分析及对比

    Android 平台上的数据库框架非常多,但是有一个共同特点就是基于对象关系映射(ORM)模型的.实现的目标也都是不需要写SQL语句,通过对对象的操作保存和操作数据.要是从语法的简洁性来说都有自己的特 ...

  5. UCOSIII钩子函数

    OSIdleTaskHook 空闲任务调用这个函数,可以用来让CPU进入低功耗模式 void OSIdleTaskHook (void) { #if OS_CFG_APP_HOOKS_EN > ...

  6. 【hbase】hbase理论学习

    HBase用途: 基于Hadoop Distributed File System,是一个开源的,基于列存储模型的分布式数据库. HBase简介: HBase是一个分布式的.多版本的.面向列的开源数据 ...

  7. HTML&CSS基础-外边框

    HTML&CSS基础-外边框  作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.HTML <!DOCTYPE html> <html> <h ...

  8. linux-秘钥生成

    服务器sshd配置 #vim /etc/ssh/sshd_conf PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys # ...

  9. SonarQube中三种类型的代码规则

    https://www.cnblogs.com/guoguochong/p/9117829.html 1.概述SonarQube(sonar)是一个 开源 平台,用于 管理 源代码的 质量 . Son ...

  10. Linux系统运维相关的面试题 (问答题)

    这里给大家整理了一些Linux系统运维相关的面试题,有些问题没有标准答案,希望要去参加Linux运维面试的朋友,可以先思考下这些问题.   一.Linux操作系统知识 1.常见的Linux发行版本都有 ...