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接口 ...
随机推荐
- python 中Dict 转 Json
近期在公司须要写个小工具.运用到的python,然后须要将Dict转成Json. 之前遇到转换Json失败,然后以为复杂的Entity结构.不能用Json的库Json.dump().进行转换. 自己些 ...
- 设置Eclipse的workspace路径
首次启动Eclipse/MyEclipse时, 会弹出"Workspace Launcher"对话框, 提示设置Workspace路径. 设定好路径后, 若勾选了"Use ...
- 我的csdn博客搬家了
把csdn上的文章都给搬到我的新博客去了, 将会在新的博客上继续写相关的技术文章 欢迎訪问: http://www.kai-zhou.com
- C#:StreamReader读取.CSV文件(转换成DataTable)
using System.Data; using System.IO; /// <summary> /// Stream读取.csv文件 /// </summary> /// ...
- 【python】函数闭包
列表时可以改
- 商城项目整理(三)JDBC增删改查
商品表的增加,修改,删除,订单表的增加,确认,用户表的查看,日志表的增加,查看 商品表建表语句: create table TEST.GOODS_TABLE ( gid NUMBER not null ...
- Struts2学习---简单的数据校验、访问Web元素
1.简单的数据校验 在action里面我们已经给出了一个数据校验: public String execute() { if(user.getUsername().equals("usern ...
- 服务器端语言go之开篇分享
由于之前看过其他脚本语言,此时看服务器端语言go语法时也短短用了半天的时间,如图1所示,是个人学习go语法的目录截图,学习网址:菜鸟网站,为了个人方便学习和记忆,因此写下本篇文章,在本篇文章里我主要是 ...
- json小结和fastjson包的实际json操作
工作中,需要处理与另一方系统数据交换的问题,采用的是调用远程接口的方法,数据格式选择的是json,今天就来聊一聊json,主要分析json数据和java Bean之间的转换问题. 一.json是什么 ...
- php生成雪花图像(不美观请见谅)
<?php /* //新建图像 //雪花 @header("Content-Type:image/png"); $w = 500; $h = 500; //create ...