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. JavaScript 自己写一个 replaceAll() 函数

    JavaScript 的  replace()  方法可以在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串. 但是,只输入字符串的话,仅替换第一个字符,当然也可以用正则表达式来进行 ...

  2. SQL+C#:一次多语言混合编程的经验总结

    1.用JAVA做,采取轮询策略: 2.用sql语言+C#混合编程,采取触发策略

  3. HTTP协议复习一--认识HTTP

    HTTP 是什么 HTTP 是一个在计算机世界里专门在两点之间传输文字.图片.音频.视频等超文本数据的约定和规范. HTTP 是一个用在计算机世界里的协议,它确立了一种计算机之间交流通信的规范,以及相 ...

  4. Linux的网络不通流程

    a:xshell连不上的问题第一步:检查网络适配器,是否禁用vmware的虚拟机网卡第二步:检查vmware net8的地址是否为10.0.0.1第三步:检查系统的vmware服务是否启动第四步:检查 ...

  5. 防火墙对nginx服务器有影响

    开启防火墙后,nginx服务器可能不能正常运行. 1

  6. zabbix-web界面显示中文

    转载:https://www.cnblogs.com/miclesvic/p/6145171.html 1.确认zabbix是否开启了中文支持功能(/var/www/html/zabbix/inclu ...

  7. linux系统编程综合练习-实现一个小型的shell程序(四)

    上节中已经对后台作业进行了简单处理,基本上要实现的功能已经完了,下面回过头来,对代码进行一个调整,把写得不好的地方梳理一下,给代码加入适当的注释,这种习惯其实是比较好了,由于在开发的时候时间都比较紧, ...

  8. P2680 运输计划[二分+LCA+树上差分]

    题目描述 公元20442044 年,人类进入了宇宙纪元. L 国有 nn 个星球,还有 n-1n−1 条双向航道,每条航道建立在两个星球之间,这 n-1n−1 条航道连通了 LL 国的所有星球. 小 ...

  9. MyBatis-Plus-Generator配置

    本文仅对使用MyBatis-Plus的代码生成器配置做保存,适合使用了该插件的童鞋做参考. 内部有大量默认配置,有性趣的童鞋可以研究下源码. ps:官方文档更齐全http://mp.baomidou. ...

  10. 十五.ProtoBuf3的基础总结

    转自: https://blog.csdn.net/u011518120/article/details/54604615 定义一个消息类型 指定字段类型 分配标识号 指定字段规则 添加更多消息类型 ...