1.socket网络编程

1.1概念:

网络套接字是跨计算机网络的连接的端点。今天,计算机之间的大多数通信都基于互联网协议;因此大多数网络套接字都是Internet套接字。更准确地说,套接字是一个句柄(抽象引用),本地程序可以将其传递给网络应用程序编程接口(API)以使用该连接,例如“在此套接字上发送此数据”。

例如,发送“Hello,world!”通过TCP到地址为1.2.3.4的主机的端口80,可以获得一个套接字,将其连接到远程主机,发送字符串,然后关闭套接字。

实现一个socket至少要分以下几步,(伪代码):

1.Socket socket = getSocket(type = "TCP")  #设定好协议类型
2.connect(socket, address = "1.2.3.4", port = "80") #连接远程机器
3.send(socket, "Hello, world!") #发送消息
4.close(socket) #关闭连接

套接字API是一种应用程序编程接口(API),通常由操作系统提供,允许应用程序控制和使用网络套接字。 Internet套接字API通常基于Berkeley套接字标准。在Berkeley套接字标准中,套接字是文件描述符(文件句柄)的一种形式,由于Unix哲学“一切都是文件”,以及套接字和文件之间的类比:你可以读,写,打开和关闭。

套接字地址是IP地址和端口号的组合,很像电话连接的一端是电话号码和特定分机的组合。 套接字不需要有地址(例如仅用于发送数据),但如果程序将套接字绑定到地址,则套接字可用于接收发送到该地址的数据。 基于此地址,Internet套接字将传入的数据包传递到适当的应用程序进程或线程

Socket Families(地址簇)

socket.AF_UNIX unix本机进程间通信

socket.AF_INET IPV4 

socket.AF_INET6  IPV6

这些常量表示用于socket()的第一个参数的地址(和协议)系列。 如果未定义AF_UNIX常量,则不支持此协议。 根据系统的不同,可能会有更多常量可用。

Socket Types

socket.SOCK_STREAM  #for tcp

socket.SOCK_DGRAM  #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 #废弃了

这些常量表示套接字类型,用于socket()的第二个参数。 根据系统的不同,可能会有更多常量可用。 (只有SOCK_STREAM和SOCK_DGRAM似乎通常很有用。)

1.2 Socket 参数

(1)socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)  必会

使用给定的地址系列,套接字类型和协议号创建一个新套接字。 地址族应为AF_INET(默认值),AF_INET6,AF_UNIX,AF_CAN或AF_RDS。 套接字类型应该是SOCK_STREAM(默认值),SOCK_DGRAM,SOCK_RAW或者其他SOCK_常量之一。 协议号通常为零并且可以省略,或者在地址族是AF_CAN的情况下,协议应该是CAN_RAW或CAN_BCM之一。 如果指定了fileno,则忽略其他参数,从而返回具有指定文件描述符的套接字。 与socket.fromfd()不同,fileno将返回相同的套接字而不是重复。 这可能有助于使用socket.close()关闭分离的套接字。

(2)socket.socketpair([family[, type[, proto]]])

使用给定的地址系列,套接字类型和协议编号构建一对连接的套接字对象。 地址族,套接字类型和协议号与上面的socket()函数相同。 如果在平台上定义,则默认系列为AF_UNIX; 否则,默认为AF_INET。

(3)socket.create_connection(address[, timeout[, source_address]])

连接到侦听Internet地址(2元组(主机,端口))的TCP服务,并返回套接字对象。 这是一个比socket.connect()更高级的函数:如果host是非数字主机名,它将尝试为AF_INET和AF_INET6解析它,然后尝试依次连接到所有可能的地址,直到连接成功。 这样可以轻松编写与IPv4和IPv6兼容的客户端。
传递可选的timeout参数将在尝试连接之前设置套接字实例上的超时。 如果未提供超时,则使用getdefaulttimeout()返回的全局默认超时设置。
如果提供,则source_address必须是要连接的套接字的2元组(主机,端口)作为其源地址才能连接。 如果主机或端口分别为'或0,则将使用OS默认行为。

(4) socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0) #获取要连接的对端主机地址

(5) sk.bind(address) 必会

sk.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。

(6) sk.listen(backlog) 必会

开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5。这个值不能无限大,因为要在内核中维护连接队列

(7)sk.setblocking(bool) 必会

是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。

(8) sk.accept() 必会

接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。接收TCP 客户的连接(阻塞式)等待连接的到来

(9)sk.connect(address) 必会

连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。

(10)sk.connect_ex(address)

同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061

(11)sk.close() 必会

关闭套接字

(12)sk.recv(bufsize[,flag]) 必会

接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。

(13)sk.recvfrom(bufsize[.flag])

与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。

(14)sk.send(string[,flag]) 必会

将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。

(15)sk.sendall(string[,flag]) 必会

将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。内部通过递归调用send,将所有内容发送出去。

(16)sk.sendto(string[,flag],address)

将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。

(17)sk.settimeout(timeout) 必会

设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )

(18)sk.getpeername()  必会

返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。

(19)sk.getsockname()

返回套接字自己的地址。通常是一个元组(ipaddr,port)

(20)sk.fileno() 

套接字的文件描述符

(21)socket.sendfile(file, offset=0, count=None)

发送文件 ,但目前多数情况下并无什么卵用。

1.3 通过socket实现简单的ssh

程序流程如下:

程序代码:

socket_server端:

import socket,os

server = socket.socket()
server.bind(("localhost",9999))
server.listen()
while True:
conn,addr = server.accept() #阻塞
while True:
print("wait for new cmd:")
cmd = conn.recv(1024).decode()
if not cmd:
print("客户端已断开")
break
cmd_result = os.popen(cmd).read()
if len(cmd_result) == 0:
cmd_result = "这条命令错误"
length = len(cmd_result.encode("utf-8"))
length = len(cmd_result.encode("utf-8"))
conn.send(str(length).encode("utf-8"))
conn.recv(1024)
conn.send(cmd_result.encode("utf-8"))
server.close()

socket_client端:

import socket

client = socket.socket()
client.connect(("localhost",9999))
while True:
cmd = input("输入指令:")
if len(cmd) == 0:
continue
client.send(cmd.encode("utf-8"))
length = client.recv(1024).decode()
print("命令结果大小:",length)
receive_size = 0
receive_data = b""
client.send(b"OK")
while receive_size < int(length):
data = client.recv(1024)
receive_size += len(data)
receive_data += data
print("命令结果实际大小:",receive_size)
print(receive_data.decode())
client.close()

程序运行结果:

#server端:
wait for new cmd:
ipconfig
wait for new cmd: #client端:
输入指令:ipconfig
命令结果大小: 1680
命令结果实际大小: 1680 Windows IP 配置 以太网适配器 以太网: 媒体状态 . . . . . . . . . . . . : 媒体已断开连接
连接特定的 DNS 后缀 . . . . . . . : 无线局域网适配器 本地连接* 3: 媒体状态 . . . . . . . . . . . . : 媒体已断开连接
连接特定的 DNS 后缀 . . . . . . . : 无线局域网适配器 本地连接* 12: 媒体状态 . . . . . . . . . . . . : 媒体已断开连接
连接特定的 DNS 后缀 . . . . . . . : 以太网适配器 VMware Network Adapter VMnet1: 连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::7ddd:a3e4:9673:512e%7
IPv4 地址 . . . . . . . . . . . . : 192.168.74.1
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . : 以太网适配器 VMware Network Adapter VMnet8: 连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::4cc1:5dc2:37f:7e7b%17
IPv4 地址 . . . . . . . . . . . . : 192.168.43.1
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . : 无线局域网适配器 WLAN: 连接特定的 DNS 后缀 . . . . . . . :
IPv6 地址 . . . . . . . . . . . . : 2001:da8:215:8f01:8d1d:db29:3fd2:c6d6
临时 IPv6 地址. . . . . . . . . . : 2001:da8:215:8f01:dc75:865e:ccf6:e781
本地链接 IPv6 地址. . . . . . . . : fe80::8d1d:db29:3fd2:c6d6%10
IPv4 地址 . . . . . . . . . . . . : 10.122.252.64
子网掩码 . . . . . . . . . . . . : 255.255.192.0
默认网关. . . . . . . . . . . . . : fe80::274:9cff:fe7d:fadb%10
10.122.192.1 输入指令:

1.4 通过socket实现简单的ftp server

1.读取文件名
2.检测文件是否存在
3.打开文件
4.检测文件大小
5.发送文件大小和md5给客户端
6.等客户端确认
7.开始边读边发数据
8.md5
9.关闭文件

程序代码:
客户端:
# -*- coding:utf-8 -*-
#!/user/bin/env.python
#Author:Mr Wu '''FTP Server'''
import socket,os,hashlib server = socket.socket()
server.bind(("localhost",1999))
server.listen()
while True:
conn,addr = server.accept() #阻塞
while True:
data = conn.recv(1024).decode()
if not data:
print("客户端已断开连接!")
break
file_cmd,file_name = data.split()
if os.path.isfile(file_name):
file_size = os.stat(file_name).st_size
conn.send(str(file_size).encode("utf-8"))
conn.recv(1024) #避免粘包
m = hashlib.md5()
f = open(file_name,"rb")
print("开始发送文件.....")
for line in f:
m.update(line)
conn.send(line)
f.close()
print("发送md5.......")
conn.send(m.hexdigest().encode("utf-8"))
server.close()
服务端:
# -*- coding:utf-8 -*-
#!/user/bin/env.python
#Author:Mr Wu import socket,os,hashlib client = socket.socket()
client.connect(("localhost",1999))
while True:
cmd = input("输入文件名[格式:get 文件名]>>>:").strip()
if len(cmd) == 0:
continue
if cmd.startswith("get"):
file_name = cmd.split()[1]
client.send(cmd.encode("utf-8"))
data = client.recv(1024)
file_total_size = int(data.decode())
client.send(b"OK")
file_size = 0
f = open(file_name,"wb")
m = hashlib.md5()
while file_size < file_total_size:
last_size = file_total_size - file_size
if last_size < 1024:
size = last_size
else:
size = 1024
'''避免粘包'''
data = client.recv(size)
m.update(data)
f.write(data)
file_size =+ len(data)
f.close()
md5 = m.hexdigest()
received_md5 = client.recv(1024).decode()
if md5 == received_md5:
print("文件md5一致,传输成功!")
else:
print("文件传输错误!")
else:
print("输入格式错误!")
continue
运行结果:
server端:
开始发送文件.....
发送md5.......
client端:
输入文件名[格式:get 文件名]>>>:get test.py
文件md5一致,传输成功!

2.socketServer的使用
2.0socketserver主要包含的类:
class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True)
它使用Internet TCP协议,该协议在客户端和服务器之间提供连续的数据流。 如果bind_and_activate为true,
则构造函数会自动尝试调用server_bind()和server_activate()。 其他参数将传递给BaseServer基类。
class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)
这使用数据报,这些数据报是可能无序到达或在传输过程中丢失的离散信息包。 参数与TCPServer相同。
class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True)
class socketserver.UnixDatagramServer(server_address, RequestHandlerClass,bind_and_activate=True)
这些不经常使用的类类似于TCP和UDP类,但使用Unix域套接字; 它们不适用于非Unix平台。 参数与TCPServer相同。
这四个类同步处理请求; 必须在下一个请求开始之前完成每个请求。 如果每个请求需要很长时间才能完成,这是不合适的,
因为它需要大量计算,或者因为它返回了客户端处理速度慢的大量数据。 解决方案是创建一个单独的进程或线程来处理每个请求;
ForkingMixIn和ThreadingMixIn混合类可用于支持异步行为。
继承图中有五个类,其中四个代表四种类型的同步服务器:

2.1 使用方法:
(1)你必须自己创建一个处理类,并且这个类要继承BaseRequestHandler,并且还要重写父类里的handle()
(2)你必须实例化TCPServer,并且传递server ip和你上面创建的请求处理类给这个TCPServer
(3)server.handle_request() #只处理一个请求
server.serve_forever() #处理多个连接请求,永远执行
2.2 SocketServer程序示例:

# -*- coding:utf-8 -*-
#!/user/bin/env.python
#Author:Mr Wu import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
'''每一个请求过来都会实例化MyTCPHandler'''
def handle(self):
'''与客户端所有的交互都是在handle里完成的'''
while True:
try:
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
#if not self.data:
# print("客户端断开连接")
# break
#just send the same data,but upper-cased
self.request.send(self.data.upper())
except ConnectionResetError as e:
print(e)
break
if __name__ == "__main__":
HOST,PORT = "localhost",9999
#create the server,binding to localhost on port 9999
server = socketserver.ThreadingTCPServer((HOST,PORT),MyTCPHandler)
'''
activate the server:this will keep running until you
interrupt the program with Ctrl-C
'''
server.serve_forever() #处理多个请求,即可以连接多个客户端
未完待续。。。。。。。。


python第八周:socket网络编程的更多相关文章

  1. 从零开始学Python第八周:网络编程基础(socket)

    Socket网络编程 一,Socket编程 (1)Socket方法介绍 Socket是网络编程的一个抽象概念.通常我们用一个Socket表示"打开了一个网络链接",而打开一个Soc ...

  2. Python全栈【Socket网络编程】

    Python全栈[socket网络编程] 本章内容: Socket 基于TCP的套接字 基于UDP的套接字 TCP粘包 SocketServer 模块(ThreadingTCPServer源码剖析) ...

  3. Python面向对象进阶和socket网络编程-day08

    写在前面 上课第八天,打卡: 为什么坚持?想一想当初: 一.面向对象进阶 - 1.反射补充 - 通过字符串去操作一个对象的属性,称之为反射: - 示例1: class Chinese: def __i ...

  4. Python面向对象进阶和socket网络编程

    写在前面 为什么坚持?想一想当初: 一.面向对象进阶 - 1.反射补充 - 通过字符串去操作一个对象的属性,称之为反射: - 示例1: class Chinese: def __init__(self ...

  5. Python之旅Day8 socket网络编程

    socket网络编程 Socket是网络编程的一个抽象概念.通常我们用一个Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可.soc ...

  6. NO.8:自学python之路------并行socket网络编程

    摘要 一到放假就杂事很多,这次的作业比较复杂,做了一个周,进度又拖了.不过结果还不错. 正文 粘包 在上一节中,如果连续发送过多数据,就可能发生粘包.粘包就是两次发送的数据粘在一起被接收,损坏了数据的 ...

  7. python3.x 基础八:socket网络编程

    Socket socket就是一直以来说的“套接字”,用于描述:ip:端口,是通信链的句柄,客户端通过这个句柄进行请求和响应 普通文件的操作顺序:打开-读写-关闭,针对的是文件 socket是特殊的文 ...

  8. Python之路【第七篇】python基础 之socket网络编程

    本篇文章大部分借鉴 http://www.cnblogs.com/nulige/p/6235531.html python socket  网络编程 一.服务端和客户端 BS架构 (腾讯通软件:ser ...

  9. Python Socket 网络编程

    Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页.QQ ...

随机推荐

  1. 霸气側漏的HTML5--之--强大的form表单

    今天学习了一下html5,发现他真的太强大了,暂不说新增的画布,通信,本地存储等的炸天功能,就连表单也是异常的好用.忍不住发一篇博客和大家分享一下.原谅我标题党了.以后的html5的学习记录博文就以& ...

  2. 揭秘传智播客班级毕业薪资超7k的内幕系列之四----汽车工的华丽转身

         ---不是本科毕业?不是计算机专业?做过电子厂?做过数控?看传智中专生侃项目,"侃晕"项目经理.从流水线上华丽转身,8.5k高薪再就业      系列三承诺写写上海传智J ...

  3. COCOS学习笔记--Button类及其相关控件属性

    一.Button介绍 Button就是button.Cocos中提供了Button类对button进行相关的操作.我们看一下Button类继承关系图: 能够看到.Button是继承自Widget类,W ...

  4. oop_day02_类、重载_20150810

    oop_day02_类.重载_20150810 1.怎样创建类?怎样创建对象? 2.引用类型之间画等号: 家门钥匙 1)指向同一个对象(数据有一份) 2)对当中一个引用的改动.会影响另外一个引用 基本 ...

  5. Codeforces Round #332 (Div. 2)C. Day at the Beach 树状数组

    C. Day at the Beach   One day Squidward, Spongebob and Patrick decided to go to the beach. Unfortuna ...

  6. Android多级目录树

    本例中目录树的菜单数据是从json数据中获取,首先建立一个菜单实体类  MenuTree package com.gao.tree; /** * 菜单树的各级菜单实体类 * * @author tjs ...

  7. aspectC++常用命令

    常用命令:1.ag++ main.cc //在工程目录下产生编译后的exe2.ag++ main.cc --weave_only //产生.acc 纯c++文件3.ag++ main.cc --gen ...

  8. 使用 Polyfill 而不再是 bable 来实践js新特性

    现状 我们想要用ES6 语法来写 JavaScript.然而由于我们需要兼容老版本的浏览器,那些浏览器不支持 ES6,我们需要解决这个问题. 有一个标准的做法是:写 ES6 代码 → 将所有代码编译成 ...

  9. idea使用svn

    一.在idea中配置svn 二.导出maven项目 连接svn 点击checkout 点击ok就可以 到Commit Changes 这里有几个选项需要了解的: Auto-update after c ...

  10. android ui篇

    android ui篇主要做两件事情. 第一件事情就是能够自己去定义基本的简单的界面. 第二件事情就是能够使用开源library去构造一些复杂的界面. 第一件事情就需要对于布局等方面知识有着基本的掌握 ...