网络编程socket
socket
socket解释
socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。
应用程序通常通过"套接字"向网络发出请求或者应答网络请求。说白了就是一种通信机制。
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。
socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)
socket和file的区别:
file模块是针对某个指定文件进行【打开】【读写】【关闭】
socket模块是针对 服务器端 和 客户端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()
套接字的文件描述符
s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。
s.listen(backlog) 开始监听传入连接。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。 s.connect(address) 连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接同一台机器上的服务器,可以将hostname设为‘localhost’。如果连接出错,返回socket.error错误。 s.connect_ex(adddress) 功能与connect(address)相同,但是成功返回0,失败返回errno的值。 s.accept() 接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。 s.close() 关闭套接字。 s.fileno() 返回套接字的文件描述符。 s.getpeername() 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。 s.getsockname() 返回套接字自己的地址。通常是一个元组(ipaddr,port) s.getsockopt(level,optname[.buflen]) 返回套接字选项的值。 s.gettimeout() 返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。 s.recv(bufsize[,flag]) 接受套接字的数据。数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。 s.recvfrom(bufsize[.flag]) 与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 s.send(string[,flag]) 将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。 s.sendall(string[,flag]) 将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。 s.sendto(string[,flag],address) 将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。 s.setblocking(flag) 如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。 s.setsockopt(level,optname,value) 设置给定套接字选项的值。 s.settimeout(timeout) 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())普通的非套接字实例的函数 getdefaulttimeout()返回默认的套接字超时时间(以秒为单位)。None表示不设置任何超时时间。 gethostbyname(hostname) 将主机名(如“www.baidu.com”)转换为IPv4地址,IP地址将以字符串的形式返回,如“8.8.8.8”。不支持IPv6 gethostname() 返回本地机器的主机名。
更多功能
建立连接
单一的服务端与客户端建立连接
server 服务端
import socket sk = socket.socket()
sk.bind(('127.0.0.1', 9999,))
sk.listen(5) while True:
conn,address = sk.accept()
print(address,conn)
client 客户端
import socket obj = socket.socket() obj.connect(('127.0.0.1', 9999,)) obj.close()
实例:
(聊天机器人)
服务端 :接受客户端的请求并返回给客户端数据。完成对话(聊天机器人)
import socket sk = socket.socket()
sk.bind(('127.0.0.1', 9999,))
sk.listen(5) while True:
conn,address = sk.accept()
conn.sendall(bytes('欢迎致电老男孩', encoding='utf-8'))
while True:
ret_bytes = conn.recv(1024)
ret_str = str(ret_bytes,encoding='utf-8')
if ret_str == 'q':
break
conn.sendall(bytes(ret_str + "你好", encoding='utf-8'))
客户端:发送内容服务端会返回数据
import socket obj = socket.socket() obj.connect(('127.0.0.1', 9999,)) ret_bytes = obj.recv(1024)
ret_str = str(ret_bytes, encoding='utf')
print(ret_str)
while True:
inp = input('请输入要发送的内容:')
if inp =='q':
obj.sendall(bytes(inp, encoding='utf-8'))
break
else:
obj.sendall(bytes(inp, encoding='utf-8'))
ret = str(obj.recv(1024), encoding='utf-8')
print(ret)
obj.close()
FTP上传
服务端
发送返回值,防止粘包
import socket sk = socket.socket() #创建服务器套接字
sk.bind(('127.0.0.1',9999,)) #绑定ip和端口,是元组
sk.listen(5) #监听链接;数值是表示一次性最多监听5个 while True:
#连接,客户端地址信息
conn,address =sk.accept() #在此阻塞,等待客户端的发送请求。accept阻塞
conn.sendall(bytes("欢迎登录老男孩FTP", encoding='utf-8')) #sendall表示发送数据,给客户端(只能发送字节)
#先接收文件大小,然后再开始接收
file_size = str(conn.recv(1024), encoding='utf-8') #recv接受数据 conn.sendall(bytes("ack",encoding='utf-8')) ###发送一个返回值,进行一次交互,防止粘包 # print(file_size)
total_size = int(file_size) #文件总大小
has_recv = 0 #已经接收的文件大小
f = open('new.png','wb')
#接收文件内容,直到获取完毕
while True:
if total_size == has_recv:
break
data = conn.recv(1024) #接收
f.write(data)
has_recv += len(data) f.close() #关闭服务端
客户端
接受服务端发来的返回值,防止粘包
import socket
import os obj = socket.socket() #创建客户端套接字
obj.connect(('127.0.0.1', 9999)) #尝试连接服务器 ret_bytes = obj.recv(1024) #在此阻塞,等待服务端发送数据。recv接收数据
ret_str = str(ret_bytes, encoding='utf-8')
print(ret_str) size = os.stat('f.png').st_size
obj.sendall(bytes(str(size),encoding='utf-8')) #发送 obj.recv(1024) ####接收服务端发来的返回值,防止粘包。 with open('f.png', 'rb') as f:
for line in f:
obj.sendall(line) #发送数据 obj.close() #关闭客户端
IO多路复用
I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
服务端
import socket sk1 = socket.socket()
sk1.bind(('127.0.0.1',8801))
sk1.listen() sk2 = socket.socket()
sk2.bind(('127.0.0.1',8802))
sk2.listen() sk3 = socket.socket()
sk3.bind(('127.0.0.1',8803))
sk3.listen() inputs = [sk1,sk2,sk3, ] import select
while True:
# #[sk1,sk2,sk3],selsct内部自动监听sk1,sk2,sk3三个对象,一旦某个句柄发生变化
# #如果有连接 sk1
# #r_list = [sk1,sk2,sk3]
# r_list,w_list,e_list = select.select(inputs,[],[],1)
# for sk in r_list:
# #每一个连接对象
# conn,address = sk.accept()
# conn.sendall(bytes("hello",encoding="utf-8"))
# conn.close() r_list, w_list, e_list = select.select(inputs, [], inputs, 1) print('正在监听的socket对象%d' % len(inputs))
print(r_list)
for sk1_or_conn in r_list:
#每一个连接对象
if sk1_or_conn == sk1:
#表示有新用户来连接
conn, address = sk1_or_conn.accept()
inputs.append(conn)
else:
#有老用户发消息
try:
data_bytes = sk1_or_conn.recv(1024)
data_str = str(data_bytes,encoding="utf-8")
sk1_or_conn.sendall(bytes(data_str+"好",encoding="utf-8"))
except Exception as ex:
inputs.remove(sk1_or_conn)
客户端1
import socket obj = socket.socket()
obj.connect(("127.0.0.1", 8801)) content = str(obj.recv(1024),encoding="utf-8")
print(content) obj.close()
客户端2
import socket obj = socket.socket()
obj.connect(("127.0.0.1", 8802)) content = str(obj.recv(1024),encoding="utf-8")
print(content) obj.close()
读写分离
服务端
import socket sk1 = socket.socket()
sk1.bind(('127.0.0.1',8801))
sk1.listen() sk2 = socket.socket()
sk2.bind(('127.0.0.1',8802))
sk2.listen() sk3 = socket.socket()
sk3.bind(('127.0.0.1',8803))
sk3.listen() inputs = [sk1,sk2,sk3, ]
outputs = []
message_dict = {} import select while True:
r_list, w_list, e_list = select.select(inputs, outputs, inputs, 1) print('正在监听的socket对象%d' % len(inputs))
print(r_list)
for sk1_or_conn in r_list:
#每一个连接对象
if sk1_or_conn == sk1:
#表示有新用户来连接
conn, address = sk1_or_conn.accept()
inputs.append(conn)
message_dict[conn] = []
else:
#有老用户发消息
try: data_str = str(data_bytes, encoding="utf-8")
message_dict[sk1_or_conn].append(data_str)
outputs.append(sk1_or_conn)
for conn in w_list:
recv_str = message_dict[conn][0]
del message_dict[conn][0]
conn.sendall(bytes(recv_str + "好",encoding="utf-8"))
outputs.remove(conn)
for sk in e_list:
inputs.remove(sk)
客户端
import socket obj = socket.socket()
obj.connect(("127.0.0.1", 8801))
while True:
inp = input('>>>')
obj.sendall(bytes(inp,encoding="utf-8"))
ret = str(obj.recv(1024),encoding="utf-8")
print(ret) obj.close()
socketserver
从而实现并发处理多个客户端请求的Socket服务端
使用:
1,创建类,必须继承(socketserver.BaseRequestHandler)
2,handle方法
3,server_forever 循环着等待着客户端连接
服务端
并发处理多个客户端请求
import socketserver #进行并发处理,接受多个客户端的请求 class MyServer(socketserver.BaseRequestHandler): def handle(self):
conn = self.request
conn.sendall(bytes('欢迎致电老男孩', encoding='utf-8'))
while True:
ret_bytes = conn.recv(1024)
ret_str = str(ret_bytes,encoding='utf-8')
if ret_str == 'q':
break
conn.sendall(bytes(ret_str + " 你好", encoding='utf-8')) if __name__ == '__main__':
server = socketserver.ThreadingTCPServer(('127.0.0.1', 9999),MyServer) #MYserver就是创建的类
server.serve_forever() #循环着等待着客户端连接
客户端
多进程的与服务端连接
import socket obj = socket.socket() obj.connect(('127.0.0.1', 9999,)) ret_bytes = obj.recv(1024)
ret_str = str(ret_bytes, encoding='utf')
print(ret_str)
while True:
inp = input('请输入要发送的内容:')
if inp =='q':
obj.sendall(bytes(inp, encoding='utf-8'))
break
else:
obj.sendall(bytes(inp, encoding='utf-8'))
ret = str(obj.recv(1024), encoding='utf-8')
print(ret)
obj.close()
ThredingTCPServer分析
ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “线程”,该线程用来和客户端进行交互。
1,ThreadingTCPServer基础
- 创建一个继承自 SocketServer.BaseRequestHandler 的类
- 类中必须定义一个名称为 handle 的方法
- 启动ThreadingTCPServer
2,ThreadingTCPServer源码剖析
内部调用流程为:
- 启动服务端程序
- 执行 TCPServer.__init__ 方法,创建服务端Socket对象并绑定 IP 和 端口
- 执行 BaseServer.__init__ 方法,将自定义的继承自SocketServer.BaseRequestHandler 的类 MyRequestHandle赋值给self.RequestHandlerClass
- 执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 ...
- 当客户端连接到达服务器
- 执行 ThreadingMixIn.process_request 方法,创建一个 “线程” 用来处理请求
- 执行 ThreadingMixIn.process_request_thread 方法
- 执行 BaseServer.finish_request 方法,执行 self.RequestHandlerClass() 即:执行 自定义 MyRequestHandler 的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用 MyRequestHandler的handle方法)
文件续传
服务端
import socket
import os BASE_DIR=os.path.dirname(os.path.abspath(__file__)) #文件名的上一级目录 home=os.path.join(BASE_DIR,"home/yuan") #在拼接路径
ip_port=("127.0.0.1",8998)
sk=socket.socket()
sk.bind(ip_port)
sk.listen(5) while True:
print("waiting ....")
conn,addr=sk.accept()
conn.sendall(bytes("欢迎登录","utf8"))
flag=True
while flag:
client_bytes=conn.recv(1024) #拿到客户端发来的文件名文件大小文件路径这三个参数
client_str=str(client_bytes,"utf8") #把获得的字节转换字符串 func,file_name,file_byte_size,target_path=client_str.split("|",3) #分解发来的三个参数
file_byte_size=int(file_byte_size) #
path=os.path.join(home,file_name) #拼接的路径 has_received=0 if os.path.exists(path): #判断是否有文件
conn.sendall(bytes("","utf8")) #返回一个值做一次交互,表示文件存在
is_continue=str(conn.recv(1024),"utf8") #等待客户端操作
if is_continue=="":
has_file_size=os.stat(path).st_size #得到上次上次传送的文件大小
conn.sendall(bytes(str(has_file_size),"utf8")) #服务端发送给客户端,告诉上次传送的文件大小
has_received+=has_file_size #把之前上传的文件与续传的文件进行想加 f=open(path,"ab") #打开文件进行追加
else:
f=open(path,"wb") #否则就进行写入
else:
conn.sendall(bytes("","utf8")) #
f=open(path,"wb") while has_received<file_byte_size: #判断发送过的文件与要上传文件进行比较,如果小于文件的大小就一直进行上传直到上传完成
try:
data=conn.recv(1024) #对出现客户端进行中断的处理
if not data:
raise Exception
except Exception:
flag=False
break f.write(data)
has_received+=len(data)
print("ending")
f.close()
客户端
import socket
import re,os,sys ip_port=("127.0.0.1",8998)
sk=socket.socket()
sk.connect(ip_port)
print(str(sk.recv(1024),"utf8")) #def bar(num=1, sum=100): # rate = float(num) / float(sum)
# rate_num = int(rate * 100)
# temp = '\r%d %%' % (rate_num, )
# sys.stdout.write(temp)
# sys.stdout.flush()
while True:
inp=input("please input:").strip() #拿到输入值
func,file_path=inp.split("|",1) #用|分开方法名和路径
local_path,target_path=re.split("\s*",file_path,1) #通过正则的\s*(空格) 一个或是多个空格分开本地路径和目标路径
file_byte_size=os.stat(local_path).st_size #获取文件大小
file_name=os.path.basename(local_path) #拿到文件名称 post_info="post|%s|%s|%s"%(file_name,file_byte_size,target_path) #拼接文件名、文件大小和文件路径
sk.sendall(bytes(post_info,"utf8")) #拼接后上传到服务端 result_exist=str(sk.recv(1024),"utf8") #接受服务端传过来的文件存在的返回值
has_sent=0
if result_exist=="":
inp=input("文件存在,是否续传?Y/N").strip()
if inp.upper()=="Y": #
sk.sendall(bytes("","utf8")) #告诉服务端进行断点续传
result_continue_pos=str(sk.recv(1024),"utf8") #
print(result_continue_pos)
has_sent=int(result_continue_pos) #已经上传文件的大小 else:
sk.sendall(bytes("","utf8")) #不进行续传 file_obj=open(local_path,"rb") #在绝对路径下读取内容
file_obj.seek(has_sent) #寻找到指针的位置(上次传到哪就跳到哪,如果没有传就从开始进行读取) while has_sent<file_byte_size:
data=file_obj.read(1024)
sk.sendall(data) #发送给服务端
has_sent+=len(data) #一次最多传送1024,超过那就进行累加 #进度条
def bar(num=1, sum=100): rate = float(num) / float(sum)
rate_num = int(rate * 100)
temp = '\r%d %%' % (rate_num, )
sys.stdout.write(temp)
sys.stdout.flush() bar(has_sent,file_byte_size)
file_obj.close()
print("上传成功")
详细请参考:http://www.cnblogs.com/wupeiqi/articles/5040823.html
网络编程socket的更多相关文章
- 网络编程socket基本API详解(转)
网络编程socket基本API详解 socket socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信. socket ...
- Android 网络编程 Socket
1.服务端开发 创建一个Java程序 public class MyServer { // 定义保存所有的Socket,与客户端建立连接得到一个Socket public static List< ...
- 网络编程Socket之TCP之close/shutdown具体解释(续)
接着上一篇网络编程Socket之TCP之close/shutdown具体解释 如今我们看看对于不同情况的close的返回情况和可能遇到的一些问题: 1.默认操作的close 说明:我们已经知道writ ...
- 铁乐学Python_Day33_网络编程Socket模块1
铁乐学Python_Day33_网络编程Socket模块1 部份内容摘自授课老师的博客http://www.cnblogs.com/Eva-J/ 理解socket Socket是应用层与TCP/IP协 ...
- Python网络编程socket
网络编程之socket 看到本篇文章的题目是不是很疑惑,what is this?,不要着急,但是记住一说网络编程,你就想socket,socket是实现网络编程的工具,那么什么是socket,什么是 ...
- java网络编程socket\server\TCP笔记(转)
java网络编程socket\server\TCP笔记(转) 2012-12-14 08:30:04| 分类: Socket | 标签:java |举报|字号 订阅 1 TCP的开销 a ...
- linux网络编程-socket(37)
在编程的时候需要加上对应pthread开头的头文件,gcc编译的时候需要加了-lpthread选项 第三个参数是线程的入口参数,函数的参数是void*,返回值是void*,第四个参数传递给线程函数的参 ...
- python网络编程-socket编程
一.服务端和客户端 BS架构 (腾讯通软件:server+client) CS架构 (web网站) C/S架构与socket的关系: 我们学习socket就是为了完成C/S架构的开发 二.OSI七层 ...
- Python开发【第八篇】:网络编程 Socket
Socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. sock ...
- python网络编程socket /socketserver
提起网络编程,不同于web编程,它主要是C/S架构,也就是服务器.客户端结构的.对于初学者而言,最需要理解的不是网络的概念,而是python对于网络编程都提供了些什么模块和功能.不同于计算机发展的初级 ...
随机推荐
- 数据接口管理工具 thx RAP
RAP是数据接口管理工具.在开发时前端将请求转至RAP,由RAP提供模拟数据:而后端使用RAP测试接口的正确性.这样RAP就成为了开发过程中的强 依赖,进而确保接口文档的实时正确性.RAP采用JSON ...
- 【转载】springMVC表单校验+全注解
在这篇文章中,我们将学习如何使用Spring表单标签, 表单验证使用 JSR303 的验证注解,hibernate-validators,提供了使用MessageSource和访问静态资源(如CSS, ...
- Jfinal中定时器的初步探索(二)
第一篇中增加的是程序代码的实现,本篇我们将通过配置文件进行定时器的配置,减少代码量,提高灵活性. 1.需要用到的文件:quartz.properties,据说这个文件如果没有的话,按默认的走,结果布署 ...
- mysql log
mysql binlog3种格式,row,mixed,statement. 解析工作 mysqlbinlog --base64-output=DECODE-ROWS -v mysql-bin.0001 ...
- get utc+8 当时时间
/// <summary> /// get utc+8 当时时间 /// </summary> /// <returns></returns> publ ...
- 关于学习Perl
Perl是一门很有用的语言,可以用它来做很多事.然而,它也仅是一门语言,掌握了Perl,你只是掌握了Computer领域的一小块知识.在学习Perl前,请明确你的学习目的,并采用正确的学习方法和资源. ...
- ASP.NET ViewState详解
ASP.NET ViewState详解[转载] 作者:Infinities Loop 概述 ViewState是一个被误解很深的动物了.我希望通过此文章来澄清人们对ViewState的一些错误认识.为 ...
- 3. redis的超时,事务,watch
一. 键的生存时间 expire设置一个已经存在的key的生存时间,到时间后redis自动删除这个 命令 expire 设置生存时间(单位/秒) pexpire设置生存时间(单位/毫秒) ttl/pt ...
- python 读取sqlite3 数据库
import sqlite3 name = "tom" age = 30 con = sqlite3.connect("d:\\test.db") cur = ...
- svn 提交错误 400 Bad Reqest MKACTIVITY 请求于XX失败 Conflict Unable to connect to a repository at URL
思路来源:http://www.cnblogs.com/wangyt223/archive/2012/11/22/2782801.html svn 提交错误 400 Bad Reqest MKACTI ...