Python网络编程篇之socket
1 socket
插座?呵呵,想多了,翻译过来意思是套接字!
A network socket is an internal endpoint for sending or receiving data at a single node in a computer network. Concretely, it is a representation of this endpoint in networking software (protocol stack), such as an entry in a table (listing communication protocol, destination, status, etc.), and is a form of system resource.
参数一:地址簇
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 ,则系统就会根据地址格式和套接类别,自动选择一个合适的协议
3.面向连接的套接字也称为虚拟电路或流套接字
面向连接的通信提供序列化的、可靠的和不重复的数据交付,并且没有记录边界,实现这种连接的主要协议是TCP(传输控制协议)。
创建TCP套接字必须使用SOCK_STREAM作为套接字类型。
无连接的传输无法保证传输的内容的顺序性、可靠性。无连接传输的优势是没有维护虚拟电路连接带来的开销,从而拥有更低的成本。实现无连接的主要协议是UDP(用户数据报协议),创建UDP套接字必须使用SOCK_DGRAM作为套接字类型。
4.Socket 参数介绍
sk.bind(address)
s.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 ,连接失败时候返回编码,例如:10061 sk.close()
关闭套接字 sk.recv(bufsize[,flag])
接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。 sk.recvfrom(bufsize[.flag])
与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 sk.send(string[,flag])
将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。 sk.sendall(string[,flag])
将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。 内部通过递归调用send,将所有内容发送出去。 sk.sendto(string[,flag],address)
将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。 sk.settimeout(timeout)
设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s ) sk.getpeername()
返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。 sk.getsockname()
返回套接字自己的地址。通常是一个元组(ipaddr,port) sk.fileno()
套接字的文件描述符
5.创建socket
socket.socket(AddressFamily, Type)
函数 socket.socket 创建⼀个 socket, 返回该 socket 的描述符, 该函数带有两个参数:
Address Family: 可以选择 AF_INET( ⽤于 Internet 进程间通信) 或者AF_UNIX( ⽤于同⼀台机器进程间通信) ,实际⼯作中常⽤AF_INET
Type: 套接字类型, 可以是 SOCK_STREAM( 流式套接字, 主要⽤于TCP 协议) 或者 SOCK_DGRAM( 数据报套接字, 主要⽤于 UDP 协议)创建⼀个tcp socket( tcp套接字)
5.1创建⼀个tcp socket( tcp套接字)
TCP通信需要建立一个可靠连接的过程,而且通信双方以流的形式发送数据。
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket Created'
tcp_server
# -*- coding: utf-8 -*-
# 2017/11/25 16:31
import socket
import threading
import time
def dealClient(sock, addr):
#第四步:接收传来的数据,并发送给对方数据
print('Accept new connection from %s:%s...' % addr)
sock.send(b'Hello,I am server!')
while True:
data = sock.recv(1024)
time.sleep(1)
if not data or data.decode('utf-8') == 'exit':
break
print('-->>%s!' % data.decode('utf-8'))
sock.send(('Loop_Msg: %s!' % data.decode('utf-8')).encode('utf-8'))
#第五步:关闭套接字
sock.close()
print('Connection from %s:%s closed.' % addr) if __name__=="__main__":
#第一步:创建一个基于IPv4和TCP协议的Socket
# 套接字绑定的IP(127.0.0.1为本机ip)与端口
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('127.0.0.1', 9999))
#第二步:监听连接
s.listen(5)
print('Waiting for connection...')
while True:
# 第三步:接受一个新连接:
sock, addr = s.accept()
# 创建新线程来处理TCP连接:
t = threading.Thread(target=dealClient, args=(sock, addr))
t.start()
tcp_client
# -*- coding: utf-8 -*-
# 2017/11/25 16:32
import socket
#初始化Socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#连接目标的ip和端口
s.connect(('127.0.0.1', 9999))
# 接收消息
print('-->>'+s.recv(1024).decode('utf-8'))
# 发送消息
s.send(b'Hello,I am a client')
print('-->>'+s.recv(1024).decode('utf-8'))
s.send(b'exit')
#关闭套接字
s.close()
5.2创建⼀个udp socket( udp套接字)
使用UDP协议时,不需要建立连接,只需要知道对方的ip和port,就可以直接发数据包,但是不关心是否能到达目的端。
UDP --- 用户数据报协议, 是一个无连接的简单的面向数据报的运输层协议。
UDP不提供可靠性, 它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。
由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,传输速度很快。
UDP是一种面向无连接的协议, 每个数据报都是一个独立的信息,包括完整的源地址或目的的地址,
它在网络上以任何可能的路径传往目的地, 因此能否到达⽬的地, 到达目的地的时间以及内容的正确性都是不能被保证的。
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print 'Socket Created'
udp_server
# -*- coding: utf-8 -*-
# 2017/11/25 16:38
import socket
#创建Socket,绑定指定的ip和端口
#SOCK_DGRAM指定了这个Socket的类型是UDP。绑定端口和TCP一样。
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('127.0.0.1', 9999))
print('Bind UDP on 9999...')
while True:
# 直接发送数据和接收数据
data, addr = s.recvfrom(1024)
print('Received from %s:%s.' %(addr,data))
s.sendto(b'Hello',addr)
udp_client
# -*- coding: utf-8 -*-
# 2017/11/25 16:39
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [b'Hello', b'World']:
# 发送数据:
s.sendto(data, ('127.0.0.1', 9999))
# 接收数据:
print(s.recv(1024).decode('utf-8'))
s.close()
一下均基于tcp开发:
一对一
server
#__author: greg
#date: 2017/9/16 16:11
import socket
ip_port = ('127.0.0.1',8888)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)#最大排队数,能开多少人
print ("服务端启动...")
# conn,addr= sk.accept()
# while True:
# client_data=conn.recv(1024)
# if str(client_data,"utf8")=='exit':
# break
# print (str(client_data,"utf8"))
# server_response=input(">>>")
# conn.sendall(bytes(server_response,"utf8"))
# conn.close() while True:
conn,address = sk.accept()
print(address)
while True:
try:
client_data=conn.recv(1024)
except:
print("意外中断")
break
print (str(client_data,"utf8"))
server_response=input(">>>")
conn.sendall(bytes(server_response,"utf8"))
conn.close()
client
#__author: greg
#date: 2017/9/16 16:11
import socket
ip_port = ('127.0.0.1',8888)
sk = socket.socket()
sk.connect(ip_port)
print ("客户端启动:")
# while True:
# inp = input('>>>')
# sk.sendall(bytes(inp,"utf8"))
# if inp == 'exit':
# break
# server_response=sk.recv(1024)
# print (str(server_response,"utf8"))
# sk.close() while True:
inp=input('>>>')
if inp=="exit":
break
sk.send(bytes(inp,'utf8'))
data=sk.recv(1024)
print(str(data,"utf8"))
sk.close()
一对多,简单并发
server
#__author: greg
#date: 2017/9/16 16:27
import socketserver
class MyServer(socketserver.BaseRequestHandler):
def handle(self):
print ("服务端启动...")
while True:
conn = self.request
print (self.client_address)
while True:
client_data=conn.recv(1024)
print (str(client_data,"utf8"))
print ("waiting...")
conn.sendall(client_data)
conn.close() if __name__ == '__main__':
server = socketserver.ThreadingTCPServer(('127.0.0.1',8091),MyServer)
server.serve_forever()
client
#__author: greg
#date: 2017/9/16 16:27 import socket ip_port = ('127.0.0.1',8091)
sk = socket.socket()
sk.connect(ip_port)
print ("客户端启动:")
while True:
inp = input('>>>')
sk.sendall(bytes(inp,"utf8"))
if inp == 'exit':
break
server_response=sk.recv(1024)
print (str(server_response,"utf8"))
sk.close()
聊天并发实例
server
#__author: greg
#date: 2017/9/16 20:55
import socketserver
class MyServer(socketserver.BaseRequestHandler):
def handle(self): #handle父类有handle方法
print ("服务端启动...")
while True:
conn = self.request
print (self.client_address)
while True:
client_data=conn.recv(1024)
print (str(client_data,"utf8"))
print ("waiting...")
server_response=input(">>>")
conn.sendall(bytes(server_response,"utf8"))
# conn.sendall(client_data)
conn.close()
# print self.request,self.client_address,self.server if __name__ == '__main__':
server = socketserver.ThreadingTCPServer(('127.0.0.1',8098),MyServer)
server.serve_forever()
client
#__author: greg
#date: 2017/9/16 20:54
import socket
ip_port = ('127.0.0.1',8098)
sk = socket.socket()
sk.connect(ip_port)
print ("客户端启动:")
while True:
inp = input('>>>')
sk.sendall(bytes(inp,"utf8"))
server_response=sk.recv(1024)
print (str(server_response,"utf8"))
if inp == 'exit':
break
sk.close()
应用:
传送命令:
cmd_server:
#__author: greg
#date: 2017/9/16 22:21
import socket
ip_port = ('127.0.0.1', 8879)
sk = socket.socket()
sk.connect(ip_port)
print("客户端启动:")
while True:
inp = input('cdm:>>>').strip()
if len(inp) == 0:
continue
if inp == "q":
break
sk.sendall(bytes(inp, "utf8"))
server_response = sk.recv(1024)
print(str(server_response, "gbk"))
print('receive data size', len(server_response))
if inp == 'exit':
break
sk.close()
cmd_client
#__author: greg
#date: 2017/9/16 22:21
import socket
ip_port = ('127.0.0.1', 8879)
sk = socket.socket()
sk.connect(ip_port)
print("客户端启动:")
while True:
inp = input('cdm:>>>').strip()
if len(inp) == 0:
continue
if inp == "q":
break
sk.sendall(bytes(inp, "utf8"))
server_response = sk.recv(1024)
print(str(server_response, "gbk"))
print('receive data size', len(server_response))
if inp == 'exit':
break
sk.close()
sendall会把数据直接全部发送到客户端,客户端将所有的数据都放到缓冲区,每次recv多少字节取决于recv内的参数,理论不应该超过8k。
所以,并不能一次recv()无限大数据,所以这里我们应该通过循环去接收。
“粘包”, 即服务器端你调用时send 2次,但你send调用时,数据其实并没有立刻被发送给客户端,而是放到了系统的socket发送缓冲区里,等缓冲区满了、或者数据等待超时了,数据才会被send到客户端,这样就把好几次的小数据拼成一个大数据,统一发送到客户端了,这么做的目地是为了提高io利用效率,一次性发送总比连发好几次效率高嘛。 但也带来一个问题,就是“粘包”,即2次或多次的数据粘在了一起统一发送。
我们在这里必须要想办法把粘包分开, 因为不分开,你就没办法取出来服务器端返回的命令执行结果的大小。
首先你是没办法让缓冲区强制刷新把数据发给客户端的。
你能做的,只有一个。就是,让缓冲区超时,超时了,系统就不会等缓冲区满了,会直接把数据发走,因为不能一个劲的等后面的数据呀,等太久,会造成数据延迟了,那可是极不好的。so如果让缓冲区超时呢?
- time.sleep(0.5),经多次测试,让服务器程序sleep 至少0.5就会造成缓冲区超时
- 不用sleep,服务器端每发送一个数据给客户端,就立刻等待客户端进行回应,即调用 conn.recv(1024), 由于recv在接收不到数据时是阻塞的,这样就会造成,服务器端接收不到客户端的响应,就不会执行后面的conn.sendall(命令结果)的指令,收到客户端响应后,再发送命令结果时,缓冲区就已经被清空了,因为上一次的数据已经被强制发到客户端了。
#__author: greg
#date: 2017/9/16 22:37
import socketserver
import subprocess
class Myserver(socketserver.BaseRequestHandler):
def handle(self):
while True:
conn=self.request
conn.sendall(bytes("欢迎登录","utf8"))
while True:
client_bytes=conn.recv(1024)
if not client_bytes:break
client_str=str(client_bytes,"utf8")
print(client_str)
command=client_str
result_str=subprocess.getoutput(command)
result_bytes = bytes(result_str,encoding='utf8')
info_str="info|%d"%len(result_bytes)
conn.sendall(bytes(info_str,"utf8"))
# conn.recv(1024)
conn.sendall(result_bytes)
conn.close()
if __name__=="__main__":
server=socketserver.ThreadingTCPServer(("127.0.0.1",9998),Myserver)
server.serve_forever()
big_server
#__author: greg
#date: 2017/9/16 22:36mysql
import socket
ip_port=("127.0.0.1",9998)
sk=socket.socket()
sk.connect(ip_port)
print("客户端启动...")
print(str(sk.recv(1024),"utf8"))
while True:
inp=input(">>>").strip()
sk.sendall(bytes(inp,"utf8"))
basic_info_bytes=sk.recv(1024)
print(str(basic_info_bytes,"utf8"))
# sk.send(bytes('ok','utf8'))
result_length=int(str(basic_info_bytes,"utf8").split("|")[1])
print(result_length)
has_received=0
content_bytes=bytes()
while has_received<result_length:
fetch_bytes=sk.recv(1024)
has_received+=len(fetch_bytes)
content_bytes+=fetch_bytes
cmd_result=str(content_bytes,"utf8")
print(cmd_result)
sk.close()
big_client
#__author: greg
#date: 2017/9/17 0:14
import socket,os ip_port=("127.0.0.1",8898)
sk=socket.socket()
sk.bind(ip_port)
sk.listen(5)
BASE_DIR=os.path.dirname(os.path.abspath(__file__))
while True:
print("waiting connect")
conn,addr=sk.accept()
flag = True
while flag:
client_bytes=conn.recv(1024)
func,file_byte_size,filename=str(client_bytes,"utf8").split("|",2)
path=os.path.join(BASE_DIR,'upload',filename)
has_received=0
file_byte_size=int(file_byte_size)
f=open(path,"wb")
while has_received<file_byte_size:
data=conn.recv(1024)
f.write(data)
has_received+=len(data)
print("ending")
f.close()
up_server
#__author: greg
#date: 2017/9/17 0:14
import socket
import re,os,sys
ip_port=("127.0.0.1",8898)
sk=socket.socket()
sk.connect(ip_port)
BASE_DIR=os.path.dirname(os.path.abspath(__file__))
print("客户端启动....")
while True:
inp=input(">>>").strip() #post|1.jpg
if inp.startswith("post"):
method,local_path=inp.split("|",1)
local_path=os.path.join(BASE_DIR,local_path)
file_byte_size=os.stat(local_path).st_size
file_name=os.path.basename(local_path)
post_info="post|%s|%s"%(file_byte_size,file_name)
sk.sendall(bytes(post_info,"utf8"))
has_sent=0
file_obj=open(local_path,"rb")
while has_sent<file_byte_size:
data=file_obj.read(1024)
sk.sendall(data)
has_sent+=len(data)
file_obj.close()
print("上传成功")
up_client
下一篇:socketserver
Python网络编程篇之socket的更多相关文章
- python 网络编程 TCP/IP socket UDP
TCP/IP简介 虽然大家现在对互联网很熟悉,但是计算机网络的出现比互联网要早很多. 计算机为了联网,就必须规定通信协议,早期的计算机网络,都是由各厂商自己规定一套协议,IBM.Apple和Micro ...
- Python网络编程篇之socketserver
1.socketserver模块和类 socketserver是标准库中的一个高级模块,目标是简化很多样板代码(创建网络客户端和服务器所必须的代码) 这个模块封装了socket编程所需要的各种各样的类 ...
- Python网络编程篇之select和epoll
1. select 原理 在多路复⽤的模型中, ⽐较常⽤的有select模型和epoll模型. 这两个都是系统接⼝, 由操作系统提供. 当然, Python的select模块进⾏了更⾼级的封装. ⽹络 ...
- python 网络编程篇
基础模拟通话网络程序: #客户端 import socket client = socket.socket() client.connect(('localhost',6969)) client.se ...
- python网络编程基础之socket粘包现象
粘包现象两种 登陆 #服务端import json import socket server=socket.socket()#创建socket对象 ip_port=('127.0.0.1',8001) ...
- python 网络编程:socket
在学习socket之前,我们先复习下相关的网络知识. OSI七层模型:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层.OSI七层模型是由国际标准化组织ISO定义的网络的基本结构,不仅包括一 ...
- Python网络编程socket
网络编程之socket 看到本篇文章的题目是不是很疑惑,what is this?,不要着急,但是记住一说网络编程,你就想socket,socket是实现网络编程的工具,那么什么是socket,什么是 ...
- 从零开始学Python第八周:网络编程基础(socket)
Socket网络编程 一,Socket编程 (1)Socket方法介绍 Socket是网络编程的一个抽象概念.通常我们用一个Socket表示"打开了一个网络链接",而打开一个Soc ...
- Day07 - Python 网络编程 Socket
1. Python 网络编程 Python 提供了两个级别访问网络服务: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口 ...
随机推荐
- 【日常学习】【IDA*】codevs2449 骑士精神题解
题目描写叙述 Description 在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位.在不论什么时候一个骑士都能依照骑士的走法(它能够走到和它横坐标相差为1.纵坐标相差为2或 ...
- 机器学习——深度学习(Deep Learning)
Deep Learning是机器学习中一个非常接近AI的领域,其动机在于建立.模拟人脑进行分析学习的神经网络,近期研究了机器学习中一些深度学习的相关知识,本文给出一些非常实用的资料和心得. Key W ...
- Esri:为Web GIS注入新内涵
纵观近些年IT与空间技术的发展,云计算.大数据.实时信息.LBS.无人机.倾斜摄影等新技术层出不穷:互联网基础设施建设成绩瞩目,宽带成为国家战略性公共基础设施. GIS(地理信息系统)作为空间信息分析 ...
- poj - 1185 炮兵阵地 状压DP 解题报告
炮兵阵地 Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 21553 Accepted: 8363 Description ...
- 【Java系列】Eclipse与Tomcat
描述 本篇文章简要讲述Eclipse和Tomcat搭建,具体包括如下内容: (1)Eclipse安装和汉化 (2)Tomcat安装和CATALIAN_HOME变量配置 (3)在Eclipse中配置To ...
- android 程序执行linux命令注意事项
一:问题描述 在已经root过的android设备下,app执行一个linux命令,app需要获取su权限,在某些android主板下会出现异常, Command: [su] Working D ...
- Subscription wildcards(MQTT)
想查看英文原文的请点击原文网址.本文是paho中讲述订阅通配符的.还是那句话,水平有限,如有翻译不当之处,欢迎指正. 订阅通配符 MQTT信息包含一个主题来进行区分.MQTT服务器使用主题来确定哪 ...
- intellij idea 下载及安装破解--好使
Intellij IDEA 14.1 提供两个版本,我下载的是旗舰版的 官方下载地址 https://www.jetbrains.com/idea/download/ 百度网盘下载地址 http:// ...
- 中国IT职业培训市场经历的几波浪潮,未来的浪潮又是那一波?
第一波 电脑普及性培训时代 2000年至2003年左右,中国正处于PC计算机普及阶段,而IT职业教育也刚开始兴起,这一波浪潮主要以计算机办公自动化.平面设计.计算机硬件维修.为主:几家大的IT培训机构 ...
- 48、mysql补充
一 视图 视图是一个虚拟表(非真实存在),其本质是[根据SQL语句获取动态的数据集,并为其命名],用户使用时只需使用[名称]即可获取结果集,可以将该结果集当做表来使用. 使用视图我们可以把查询过程中的 ...