Python黑帽编程2.8 套接字编程
套接字编程在本系列教程中地位并不是很突出,但是我们观察网络应用,绝大多数都是基于Socket来做的,哪怕是绝大多数的木马程序也是如此。官方关于socket编程的文档地址为https://docs.python.org/2/library/socket.html,我承认我看起来都很费劲。
套接字为BSD UNIX系统核心的一部分,而且他们也被许多其他类似UNIX的操作系统包括Linux所采纳。许多非BSD UNIX系统(如ms-dos,windows,os/2,mac os及大部分主机环境)都以库形式提供对套接字的支持。
三种最流行的套接字类型是:stream,datagram和raw。stream和datagram套接字可以直接与TCP协议进行接口,而raw套接字则接口到IP协议。但套接字并不限于TCP/IP。
Python 提供了两个基本的套接字模块:
l 第一个是 socket,它提供了标准的 BSD Sockets API。
l 第二个是 socketServer, 它提供了服务器中心类,可以简化网络服务器的开发。
本节课程主要关注socket模块的使用。
2.8.1 socket函数
socket函数用来创建socket对象,使用前需要导入socket模块。我们先看一下socket函数的使用方式:
socket.socket([family[, type[, proto]]])
我们看到socket函数接收三个参数,下面分别介绍:
family——套接字对象使用的地址族,可选值:AF_INET——IPv4地址族,AF_INET6——IPv6地址族,AF_UNIX——针对类UNIX系统的套接字,如图2所示。
图2
type可选参数如图3所示。
图3
proto是协议数,默认是0,通常不需要关心该参数。
熟悉了三个参数的含义,创建一个TCP或者UDP的socket对象就很容易了。
创建TCP Socket:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
创建UDP Socket:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
关于socket对象的各种方法的使用说明,大家可以去查官方文档,我这里就不贴了,下面我们边实践,边学习。
2.8.2 创建socket
我们首先来创建一个TCP的socket对象,代码如下:
#Socket client example in python
import socket #for sockets
#create an AF_INET, STREAM socket (TCP)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket Created'
创建一个socket对象很简单,第一步导入socket模块,第二步使用socket.socket方法创建一个socket对象,参数在2.8.1节已经做了说明。运行结果如下:
图4
2.8.3 客户端编程
创建socket之后,现在我们看看如何创建client端,连接一个服务端,这里我们选择知乎(www.zhihu.com)。socket类有个gethostbyname方法,可以用来把域名转换为ip地址,例如:
import socket #for sockets
import sys #for exit
try:
#create an AF_INET, STREAM socket (TCP)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error, msg:
print 'Failed to create socket. Error code: ' + str(msg[0]) + ' , Error message : ' + msg[1]
sys.exit();
print 'Socket Created'
host = 'www.zhihu.com'
try:
remote_ip = socket.gethostbyname( host )
except socket.gaierror:
#could not resolve
print 'Hostname could not be resolved. Exiting'
sys.exit()
print 'Ip address of ' + host + ' is ' + remote_ip
上面的代码,我们首先导入了sys模块,用于在出现异常的情况下调用sys.exit()退出。同时,完善了异常捕获,创建socket可能会引发socket.error异常,gethostbyname方法可能会引发socket.gaierror异常,我们在编程过程中要注意这些错误。
运行结果如下:
图5
现在我们获得了ip,端口我们使用80,现在可以连接服务器了。
import socket #for sockets
import sys #for exit
try:
#create an AF_INET, STREAM socket (TCP)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error, msg:
print 'Failed to create socket. Error code: ' + str(msg[0]) + ' , Error message : ' + msg[1]
sys.exit();
print 'Socket Created'
host = 'www.zhihu.com'
port = 80
try:
remote_ip = socket.gethostbyname( host )
except socket.gaierror:
#could not resolve
print 'Hostname could not be resolved. Exiting'
sys.exit()
print 'Ip address of ' + host + ' is ' + remote_ip
#Connect to remote server
s.connect((remote_ip , port))
print 'Socket Connected to ' + host + ' on ip ' + remote_ip
注意上面加粗的代码,使用socket.connect方法连接服务端,参数为ip地址和端口。
连接上服务器之后,现在可以向服务器发送信息了。
我们在上面代码的基础上,添加下面的代码:
#Send some data to remote server
message = "GET / HTTP/1.1\r\n\r\n"
try :
#Set the whole string
s.sendall(message)
except socket.error:
#Send failed
print 'Send failed'
sys.exit()
print 'Message send successfully'
首先定义了要发送的信息,准备发送一个http协议的get请求。随后我们使用socket.sendall方法发送信息。信息发送之后,如果没有异常产生,应该会得到知乎服务器的响应内容。那么如何接收信息呢?继续添加如下代码:
#Now receive data
reply = s.recv(4096)
print reply
我们使用socket.recv方法来接收数据,参数是接收的数据长度。现在我们来看一下运行结果:
图6
最后,接收完数据之后,不要忘了关闭socket:
s.close()
现在我们来总结下客户端socket编程的基本流程:
1. 创建套接字
2. 连接服务端
3. 发送数据
4. 接收数据
5. 关闭连接
下面看一下,一个基本的服务端,要做哪些工作。
2.8.4 服务端编程
和客户端编程类似,我们首先创建一个socket对象。
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
随后,我们要把socket绑定到本机的IP和端口上。
try:
s.bind((HOST, PORT))
except socket.error , msg:
print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
绑定ip和端口使用的是bind方法。
服务端不会主动连接其他主机,而是等待客户端连接,这需要进入监听状态。
s.listen(10)
print 'Socket now listening'
listen方法接收一个参数,用来指定可以同时挂起的连接数。
监听模式之后,如果有客户端连接进来,如何接收连接呢?需要使用accept方法。
#wait to accept a connection - blocking call
conn, addr =s.accept()
accept方法会返回一个代表当前链接的connection对象和客户端的ip地址。接下来就可以使用conn对象来接收和发送数据了,关闭连接也使用conn对象。
data = conn.recv(1024)
conn.sendall(data)
conn.close()
下面我们看一下服务端的完整代码:
import socket
import sys
HOST = '' # Symbolic name meaning all available interfaces
PORT = 8888 # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket created'
try:
s.bind((HOST, PORT))
except socket.error , msg:
print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
sys.exit()
print 'Socket bind complete'
s.listen(10)
print 'Socket now listening'
#wait to accept a connection - blocking call
conn, addr = s.accept()
print 'Connected with ' + addr[0] + ':' + str(addr[1])
#now keep talking with the client
data = conn.recv(1024)
conn.sendall(data)
conn.close()
s.close()
将代码保存为socket_server.py,从终端启动,如下图:
图7
然后再启动一个终端,使用telnet连接。
图8
此时可以看到服务端打印了连接的客户端,接下来咋telnet终端输入字符,回车发送过去。结果如图7。
图9
从图7可以看到telnet终端显示了服务端返回的信息。
2.8.5 改进服务端
作为服务端,只能接收一次连接,和客户端进行有限次数的交互是不行的,要一直能接收来自客户端的连接,下面我们改进服务端。
import socket
import sys
HOST = '' # Symbolic name meaning all available interfaces
PORT = 5000 # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket created'
try:
s.bind((HOST, PORT))
except socket.error , msg:
print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
sys.exit()
print 'Socket bind complete'
s.listen(10)
print 'Socket now listening'
#now keep talking with the client
while 1:
#wait to accept a connection - blocking call
conn, addr = s.accept()
print 'Connected with ' + addr[0] + ':' + str(addr[1])
data = conn.recv(1024)
reply = 'OK...' + data
if not data:
break
conn.sendall(reply)
conn.close()
s.close()
修改后的代码只是将接收客户端连接的代码放到一个while true循环里,这样就可以不停的接收连接。保存上面的代码,在终端打开等待连接,然后启动三个终端使用telnet进行连接,结果如下:
图10
这样改起来,可以不停的接收新的连接了,但是每次只能接收一个连接,有新连接的时候,旧连接就会断掉,还是不够完美。想要分别对待每一个新入的连接,需要多线程登场了。
import socket
import sys
from thread import *
HOST = '' # Symbolic name meaning all available interfaces
PORT = 8888 # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket created'
#Bind socket to local host and port
try:
s.bind((HOST, PORT))
except socket.error , msg:
print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
sys.exit()
print 'Socket bind complete'
#Start listening on socket
s.listen(10)
print 'Socket now listening'
#Function for handling connections. This will be used to create threads
def clientthread(conn):
#Sending message to connected client
conn.send('Welcome to the server. Type something and hit enter\n') #send only takes string
#infinite loop so that function do not terminate and thread do not end.
while True:
#Receiving from client
data = conn.recv(1024)
reply = 'OK...' + data
if not data:
break
conn.sendall(reply)
#came out of loop
conn.close()
#now keep talking with the client
while 1:
#wait to accept a connection - blocking call
conn, addr = s.accept()
print 'Connected with ' + addr[0] + ':' + str(addr[1])
#start new thread takes 1st argument as a function name to be run, second is the tuple of arguments to the function.
start_new_thread(clientthread ,(conn,))
s.close()
使用多线程,首先要导入thread模块。随后我们定义了一个名为clientthread的方法,该方法接收每个客户端连接对象,在内部启动一个新的while循环,这样可以保证客户端和服务端可以一直连接,反复通信。在外层,仍然保留while循环,每次有客户端连接的时候,生成一个连接对象conn,然后使用thread模块的start_new_thread方法启动一个新的线程,新线程调用clientthread方法,传入刚接收的客户端连接对象。
我们将上面的代码保存为socket_thread.py,然后从终端启动,在启动多个终端使用telnet进行连接,连接之后发消息测试,结果如下:
图11
2.8.6 小结
本节我们学习了Python中socket的基本使用方法,并演示了基于TCP协议的客户端和服务端编程方法。这里大家需要额外补充的知识点是多线程的使用,我们做网络扫描、爬虫等程序,都是离不开多线程应用的。
下一节是第二章的最后一节,和大家聊一聊面向对象编程思想及Python的面向对象编程方法。
第2.8节《面向对象编程思想及Python的面向对象编程方法》已经在微信订阅号抢先发布,心急的同学进入订阅号(二维码在下方),从菜单“网络安全”—>”Python黑帽编程”进入即可。
查看完整系列教程,请关注我的微信订阅号(xuanhun521,下方二维码),回复“python”。问题讨论请加qq群:Hacking (1群):303242737 Hacking (2群):147098303。
Python黑帽编程2.8 套接字编程的更多相关文章
-
linux网络编程-(socket套接字编程UDP传输)
今天我们来介绍一下在linux网络环境下使用socket套接字实现两个进程下文件的上传,下载,和退出操作! 在socket套接字编程中,我们当然可以基于TCP的传输协议来进行传输,但是在文件的传输中, ...
-
【Unix网络编程】chapter3套接字编程简介
chapter3套接字编程简介3.1 概述 地址转换函数在地址的文本表达和他们存放在套接字地址结构中的二进制值之间进行转换.多数现存的IPv4代码使用inet_addr和inet_ntoa这两个函数, ...
-
【Unix网络编程】chapter3 套接字编程简介
chapter3套接字编程简介3.1 概述 地址转换函数在地址的文本表达和他们存放在套接字地址结构中的二进制值之间进行转换.多数现存的IPv4代码使用inet_addr和inet_ntoa这两个函数, ...
-
UNIX网络编程——基本TCP套接字编程
一.基于TCP协议的网络程序 下图是基于TCP协议的客户端/服务器程序的一般流程: 服务器调用socket().bind().listen()完成初始化后,调用accept()阻塞等待,处于监听端口的 ...
-
<;网络编程>;基本TCP套接字编程
tcp提供了可靠传输,当tcp向另一端发送数据的时候,要求对端返回一个确认.如果没有接收到确认,tcp就重传数据并且等待更长时间,数次重传失败后,tcp才放弃. 建立一个tcp连接会发生如下事情: 服 ...
-
31_网络编程(Socket套接字编程)_讲义
今日内容介绍 1.网络三要素及传输协议 2.实现UDP协议的发送端和接收端 3.实现TCP协议的客户端和服务器 4.TCP上传文件案例 01网络模型 *A:网络模型 TCP/IP协议中的四层分别是应用 ...
-
Python套接字编程(1)——socket模块与套接字编程
在Python网络编程系列,我们主要学习以下内容: 1. socket模块与基本套接字编程 2. socket模块的其他网络编程功能 3. SocketServer模块与简单并发服务器 4. 异步编程 ...
-
Linux Socket 原始套接字编程
对于linux网络编程来说,可以简单的分为标准套接字编程和原始套接字编程,标准套接字主要就是应用层数据的传输,原始套接字则是可以获得不止是应用层的其他层不同协议的数据.与标准套接字相区别的主要是要开发 ...
-
入门级:怎么使用C#进行套接字编程(二)
入门级:怎么使用C#进行套接字编程(一) 原文地址如下: C# Server Socket program C# Client Socket program 代码环境:VS2010+Win8.1企业评 ...
随机推荐
-
读<;<;领域驱动设计-软件核心复杂性应对之道>;>;有感
道可道,非常道. 名可名,非常名. 无名天地之始,有名万物之母. ---老子 关于标题 好久没写东西了,动笔的动机是看完了一本书,想写点总结性的东西,一是为了回顾一下梳理知识点,二是为了日后遗忘时能有 ...
-
codeforces——Little Pony and Expected Maximum
/* 我们枚举每次选择最大数值的情况:m个数, 投掷n次 最大值是1: 1种 2: 2^n-1 3: 3^n-2^n ..... m: m^n-(m-1)^n 所以最后的结果=sum((k/m)^n ...
-
loading 加载
<script type="text/javascript" src="jquery-1.3.2.js"></script> <s ...
-
C++ struct 定义及初始化
1 定义: struct Book{ unsigned int bookNo; string name; }; 2 初始化 struct的初始化分为三种:逐一赋值,顺序赋值,乱序赋值 // 逐一 ...
-
HDOJ --1172
#include<iostream> #include<cstdio> #include<cstring> #include<string> #incl ...
-
每天一个Linux命令(07)--mv命令
mv命令是move的缩写,可以用来移动文件或者将文件改名,这也是个常用命令,经常用来备份文件或者目录. 1.命令格式: mv [选项] 源文件或目录 目标文件或目录 2.命令功能: 视mv命令中第 ...
-
Android使用百度地图出现闪退及定位时显示蓝屏问题
目录 1.Android使用百度地图出现闪退 2.Android使用百度地图定位出现蓝屏问题 1.Android使用百度地图出现闪退 一般情况下出现闪退是在AndroidManifest.x ...
-
2019Java查漏补缺(二)
查看了公众号:java之间的整理的集和文章,文章地址 总结和搜索了一下网络知识,总结了一下: 1.String 的hashcode()方法 2.switch总结: 3.如何实现克隆 1.String ...
-
【转载】LINUX 和 WINDOWS 内核的区别
LINUX 和 WINDOWS 内核的区别 [声明:欢迎转载,转载请注明出自CU ACCESSORY http://linux.chinaunix.net/bbs/thread-1153868-1-1 ...
-
pyqt5-控件是否可用
setEnabled(bool) 设置控件是否可用 True 可用 isEnabled() 获取控件是否可用 s=button.isEnabled()