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的更多相关文章

  1. 网络编程socket基本API详解(转)

    网络编程socket基本API详解   socket socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信. socket ...

  2. Android 网络编程 Socket

    1.服务端开发 创建一个Java程序 public class MyServer { // 定义保存所有的Socket,与客户端建立连接得到一个Socket public static List< ...

  3. 网络编程Socket之TCP之close/shutdown具体解释(续)

    接着上一篇网络编程Socket之TCP之close/shutdown具体解释 如今我们看看对于不同情况的close的返回情况和可能遇到的一些问题: 1.默认操作的close 说明:我们已经知道writ ...

  4. 铁乐学Python_Day33_网络编程Socket模块1

    铁乐学Python_Day33_网络编程Socket模块1 部份内容摘自授课老师的博客http://www.cnblogs.com/Eva-J/ 理解socket Socket是应用层与TCP/IP协 ...

  5. Python网络编程socket

    网络编程之socket 看到本篇文章的题目是不是很疑惑,what is this?,不要着急,但是记住一说网络编程,你就想socket,socket是实现网络编程的工具,那么什么是socket,什么是 ...

  6. java网络编程socket\server\TCP笔记(转)

    java网络编程socket\server\TCP笔记(转) 2012-12-14 08:30:04|  分类: Socket |  标签:java  |举报|字号 订阅     1 TCP的开销 a ...

  7. linux网络编程-socket(37)

    在编程的时候需要加上对应pthread开头的头文件,gcc编译的时候需要加了-lpthread选项 第三个参数是线程的入口参数,函数的参数是void*,返回值是void*,第四个参数传递给线程函数的参 ...

  8. python网络编程-socket编程

     一.服务端和客户端 BS架构 (腾讯通软件:server+client) CS架构 (web网站) C/S架构与socket的关系: 我们学习socket就是为了完成C/S架构的开发 二.OSI七层 ...

  9. Python开发【第八篇】:网络编程 Socket

    Socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. sock ...

  10. python网络编程socket /socketserver

    提起网络编程,不同于web编程,它主要是C/S架构,也就是服务器.客户端结构的.对于初学者而言,最需要理解的不是网络的概念,而是python对于网络编程都提供了些什么模块和功能.不同于计算机发展的初级 ...

随机推荐

  1. WCF Client is Open Source

    WCF Client is Open Source Wednesday, May 20, 2015 Announcement New Project WCF We’re excited to anno ...

  2. JQuery Pagenation 知识点整理——phototype 应用(20150517)(转)

    JS中的phototype是JS中比较难理解的一个部分 本文基于下面几个知识点: 1 原型法设计模式 在.Net中可以使用clone()来实现原型法 原型法的主要思想是,现在有1个类A,我想要创建一个 ...

  3. How to send Email using C#

    try { MailMessage mail = new MailMessage(); SmtpClient SmtpServer = new SmtpClient("smtp.gmail. ...

  4. Mysql 索引 转载

    转自 :http://blog.csdn.net/wud_jiyanhui/article/details/7403375 什么是索引 索引时一种特殊的文件,他们包涵着对数据表里所有记录的引用指针. ...

  5. redis报错Windows error 0x70(a large memory)

    redis报错Windows error 0x70 redis 嫌弃你内存不够了,就给你不开第二个实例. The Windows version of Redis allocates a large ...

  6. SET Transaction Isolation Level Read语法的四种情况

    转自:http://www.cnblogs.com/qanholas/archive/2012/01/04/2312152.html 存储过程:SET Transaction Isolation Le ...

  7. 电脑设置固定ip

  8. Hive(二):windows hive ODBC 安装

    针对Windows 32位和64位的系统对应有多个版本的 Hive ODBC Connector, 在安装的过程中,版本必须完全匹配(即:32位的 connector 只能在32位的系统运行,64位的 ...

  9. rm命令

    rm是一个危险的命令,使用的时候要特别当心,尤其对于新手,否则整个系统就会毁在这个命令(比如在/(根目录)下执行rm * -rf).所以,我们在执行rm之前最好先确认一下在哪个目录,到底要删除什么东西 ...

  10. CentOS 6.0最小化编译安装Nginx+MySQL+PHP+Zend

    http://www.osyunwei.com/archives/235.html 引言: 操作系统:CentOS 6.0 32位         下载地址:http://mirrors.163.co ...