Python黑帽编程2.8 套接字编程

代码不好排版,可以到微信订阅号(xuanhun521)查看原文。

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。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,050评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,538评论 1 306
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,673评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,622评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,047评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,974评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,129评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,893评论 0 209
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,654评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,828评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,297评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,619评论 3 262
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,326评论 3 243
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,176评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,975评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,118评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,909评论 2 278

推荐阅读更多精彩内容

  • http://python.jobbole.com/85231/ 关于专业技能写完项目接着写写一名3年工作经验的J...
    燕京博士阅读 7,486评论 1 118
  • 最近在学习Python看了一篇文章写得不错,是在脚本之家里的,原文如下,很有帮助: 一、网络知识的一些介绍 soc...
    qtruip阅读 2,619评论 0 6
  • 大纲 一.Socket简介 二.BSD Socket编程准备 1.地址 2.端口 3.网络字节序 4.半相关与全相...
    VD2012阅读 2,117评论 0 5
  • UIView的常见属性 NSArray *subviews所有的子控件数组元素的顺序决定着子控件的显示层级顺序(下...
    Jjianshu阅读 125评论 0 0
  • 等长大了就好了! 等成家了就好了! 等有孩子就好了! 似乎每进入一个人生阶段,人就应该要长大,随着肩负责任的加大,...
    東東_ning阅读 308评论 0 0