这是用来快速学习 Python Socket 套接字编程的指南和教程。Python 的 Socket 编程跟 C 语言很像。
Python 官方关于 Socket 的函数请看 http://docs.python.org/library/socket.html

基本上,Socket 是任何一种计算机网络通讯中最基础的内容。例如当你在浏览器地址栏中输入 www.oschina.net 时,你会打开一个套接字,然后连接到 www.oschina.net 并读取响应的页面然后然后显示出来。而其他一些聊天客户端如 gtalk 和 skype 也是类似。任何网络通讯都是通过 Socket 来完成的。

写在开头

本教程假设你已经有一些基本的 Python 编程的知识。

让我们开始 Socket 编程吧。

创建 Socket

首先要做的就是创建一个 Socket,socket 的 socket 函数可以实现,代码如下:

1 #Socket client example in python
2
3 import socket #for sockets
4
5 #create an AF_INET, STREAM socket (TCP)
6 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
7
8 print 'Socket Created'
函数 socket.socket 创建了一个 Socket,并返回 Socket 的描述符可用于其他 Socket 相关的函数。

上述代码使用了下面两个属性来创建 Socket:

地址簇 : AF_INET (IPv4)
类型: SOCK_STREAM (使用 TCP 传输控制协议)

错误处理

如果 socket 函数失败了,python 将抛出一个名为 socket.error 的异常,这个异常必须予以处理:

01 #handling errors in python socket programs
02
03 import socket #for sockets
04 import sys #for exit
05
06 try:
07 #create an AF_INET, STREAM socket (TCP)
08 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
09 except socket.error, msg:
10 print 'Failed to create socket. Error code: ' + str(msg[0]) + ' , Error message : ' + msg[1]
11 sys.exit();
12
13 print 'Socket Created'
好了,假设你已经成功创建了 Socket,下一步该做什么呢?接下来我们将使用这个 Socket 来连接到服务器。

注意

与 SOCK_STREAM 相对应的其他类型是 SOCK_DGRAM 用于 UDP 通讯协议,UDP 通讯是非连接 Socket,在这篇文章中我们只讨论 SOCK_STREAM ,或者叫 TCP 。

连接到服务器

连接到服务器需要服务器地址和端口号,这里使用的是 www.oschina.net 和 80 端口。

首先获取远程主机的 IP 地址

连接到远程主机之前,我们需要知道它的 IP 地址,在 Python 中,获取 IP 地址是很简单的:

01 import socket #for sockets
02 import sys #for exit
03
04 try:
05 #create an AF_INET, STREAM socket (TCP)
06 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
07 except socket.error, msg:
08 print 'Failed to create socket. Error code: ' + str(msg[0]) + ' , Error message : ' + msg[1]
09 sys.exit();
10
11 print 'Socket Created'
12
13 host = 'www.oschina.net'
14
15 try:
16 remote_ip = socket.gethostbyname( host )
17
18 except socket.gaierror:
19 #could not resolve
20 print 'Hostname could not be resolved. Exiting'
21 sys.exit()
22
23 print 'Ip address of ' + host + ' is ' + remote_ip
我们已经有 IP 地址了,接下来需要指定要连接的端口。

代码:

01 import socket #for sockets
02 import sys #for exit
03
04 try:
05 #create an AF_INET, STREAM socket (TCP)
06 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
07 except socket.error, msg:
08 print 'Failed to create socket. Error code: ' + str(msg[0]) + ' , Error message : ' + msg[1]
09 sys.exit();
10
11 print 'Socket Created'
12
13 host = 'www.oschina.net'
14 port = 80
15
16 try:
17 remote_ip = socket.gethostbyname( host )
18
19 except socket.gaierror:
20 #could not resolve
21 print 'Hostname could not be resolved. Exiting'
22 sys.exit()
23
24 print 'Ip address of ' + host + ' is ' + remote_ip
25
26 #Connect to remote server
27 s.connect((remote_ip , port))
28
29 print 'Socket Connected to ' + host + ' on ip ' + remote_ip
现在运行程序

1 $ python client.py
2 Socket Created
3 Ip address of www.oschina.net is 61.145.122.155
4 Socket Connected to www.oschina.net on ip 61.145.122.155
这段程序创建了一个 Socket 并进行连接,试试使用其他一些不存在的端口(如81)会是怎样?这个逻辑相当于构建了一个端口扫描器。

已经连接上了,接下来就是往服务器上发送数据。

免费提示

使用 SOCK_STREAM/TCP 套接字才有“连接”的概念。连接意味着可靠的数据流通讯机制,可以同时有多个数据流。可以想象成一个数据互不干扰的管道。另外一个重要的提示是:数据包的发送和接收是有顺序的。

其他一些 Socket 如 UDP、ICMP 和 ARP 没有“连接”的概念,它们是无连接通讯,意味着你可从任何人或者给任何人发送和接收数据包。

发送数据

sendall 函数用于简单的发送数据,我们来向 oschina 发送一些数据:

01 import socket #for sockets
02 import sys #for exit
03
04 try:
05 #create an AF_INET, STREAM socket (TCP)
06 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
07 except socket.error, msg:
08 print 'Failed to create socket. Error code: ' + str(msg[0]) + ' , Error message : ' + msg[1]
09 sys.exit();
10
11 print 'Socket Created'
12
13 host = 'www.oschina.net'
14 port = 80
15
16 try:
17 remote_ip = socket.gethostbyname( host )
18
19 except socket.gaierror:
20 #could not resolve
21 print 'Hostname could not be resolved. Exiting'
22 sys.exit()
23
24 print 'Ip address of ' + host + ' is ' + remote_ip
25
26 #Connect to remote server
27 s.connect((remote_ip , port))
28
29 print 'Socket Connected to ' + host + ' on ip ' + remote_ip
30
31 #Send some data to remote server
32 message = "GET / HTTP/1.1\r\n\r\n"
33
34 try :
35 #Set the whole string
36 s.sendall(message)
37 except socket.error:
38 #Send failed
39 print 'Send failed'
40 sys.exit()
41
42 print 'Message send successfully'
上述例子中,首先连接到目标服务器,然后发送字符串数据 "GET / HTTP/1.1\r\n\r\n" ,这是一个 HTTP 协议的命令,用来获取网站首页的内容。

接下来需要读取服务器返回的数据。

接收数据

recv 函数用于从 socket 接收数据:

01 #Socket client example in python
02
03 import socket #for sockets
04 import sys #for exit
05
06 #create an INET, STREAMing socket
07 try:
08 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
09 except socket.error:
10 print 'Failed to create socket'
11 sys.exit()
12
13 print 'Socket Created'
14
15 host = 'oschina.net';
16 port = 80;
17
18 try:
19 remote_ip = socket.gethostbyname( host )
20
21 except socket.gaierror:
22 #could not resolve
23 print 'Hostname could not be resolved. Exiting'
24 sys.exit()
25
26 #Connect to remote server
27 s.connect((remote_ip , port))
28
29 print 'Socket Connected to ' + host + ' on ip ' + remote_ip
30
31 #Send some data to remote server
32 message = "GET / HTTP/1.1\r\nHost: oschina.net\r\n\r\n"
33
34 try :
35 #Set the whole string
36 s.sendall(message)
37 except socket.error:
38 #Send failed
39 print 'Send failed'
40 sys.exit()
41
42 print 'Message send successfully'
43
44 #Now receive data
45 reply = s.recv(4096)
46
47 print reply
下面是上述程序执行的结果:

01 $ python client.py
02 Socket Created
03 Ip address of oschina.net is 61.145.122.
04 Socket Connected to oschina.net on ip 61.145.122.155
05 Message send successfully
06 HTTP/1.1 301 Moved Permanently
07 Server: nginx
08 Date: Wed, 24 Oct 2012 13:26:46 GMT
09 Content-Type: text/html
10 Content-Length: 178
11 Connection: keep-alive
12 Keep-Alive: timeout=20
13 Location: http://www.oschina.net/
oschina.net 回应了我们所请求的 URL 的内容,很简单。数据接收完了,可以关闭 Socket 了。

关闭 socket

close 函数用于关闭 Socket:

1 s.close()
这就是了。

让我们回顾一下

上述的示例中我们学到了如何:

1. 创建 Socket
2. 连接到远程服务器
3. 发送数据
4. 接收回应

当你用浏览器打开 www.oschina.net 时,其过程也是一样。包含两种类型,分别是客户端和服务器,客户端连接到服务器并读取数据,服务器使用 Socket 接收进入的连接并提供数据。因此在这里 www.oschina.net 是服务器端,而你的浏览器是客户端。

接下来我们开始在服务器端做点编码。

服务器端编程

服务器端编程主要包括下面几步:

1. 打开 socket
2. 绑定到一个地址和端口
3. 侦听进来的连接
4. 接受连接
5. 读写数据

我们已经学习过如何打开 Socket 了,下面是绑定到指定的地址和端口上。

绑定 Socket

bind 函数用于将 Socket 绑定到一个特定的地址和端口,它需要一个类似 connect 函数所需的 sockaddr_in 结构体。

示例代码:

01 import socket
02 import sys
03
04 HOST = '' # Symbolic name meaning all available interfaces
05 PORT = 8888 # Arbitrary non-privileged port
06
07 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
08 print 'Socket created'
09
10 try:
11 s.bind((HOST, PORT))
12 except socket.error , msg:
13 print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
14 sys.exit()
15
16 print 'Socket bind complete'
绑定完成后,就需要让 Socket 开始侦听连接。很显然,你不能将两个不同的 Socket 绑定到同一个端口之上。

连接侦听

绑定 Socket 之后就可以开始侦听连接,我们需要将 Socket 变成侦听模式。socket 的 listen 函数用于实现侦听模式:

1 s.listen(10)
2 print 'Socket now listening'
listen 函数所需的参数成为 backlog,用来控制程序忙时可保持等待状态的连接数。这里我们传递的是 10,意味着如果已经有 10 个连接在等待处理,那么第 11 个连接将会被拒绝。当检查了 socket_accept 后这个会更加清晰。

接受连接

示例代码:

01 import socket
02 import sys
03
04 HOST = '' # Symbolic name meaning all available interfaces
05 PORT = 8888 # Arbitrary non-privileged port
06
07 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
08 print 'Socket created'
09
10 try:
11 s.bind((HOST, PORT))
12 except socket.error , msg:
13 print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
14 sys.exit()
15
16 print 'Socket bind complete'
17
18 s.listen(10)
19 print 'Socket now listening'
20
21 #wait to accept a connection - blocking call
22 conn, addr = s.accept()
23
24 #display client information
25 print 'Connected with ' + addr[0] + ':' + str(addr[1])
输出

运行该程序将会显示:

1 $ python server.py
2 Socket created
3 Socket bind complete
4 Socket now listening
现在这个程序开始等待连接进入,端口是 8888,请不要关闭这个程序,我们来通过 telnet 程序来进行测试。

打开命令行窗口并输入:

1 $ telnet localhost 8888
2
3 It will immediately show
4 $ telnet localhost 8888
5 Trying 127.0.0.1...
6 Connected to localhost.
7 Escape character is '^]'.
8 Connection closed by foreign host.
而服务器端窗口显示的是:

1 $ python server.py
2 Socket created
3 Socket bind complete
4 Socket now listening
5 Connected with 127.0.0.1:59954
我们可看到客户端已经成功连接到服务器。

上面例子我们接收到连接并立即关闭,这样的程序没什么实际的价值,连接建立后一般会有大量的事情需要处理,因此让我们来给客户端做出点回应吧。

sendall 函数可通过 Socket 给客户端发送数据:

01 import socket
02 import sys
03
04 HOST = '' # Symbolic name meaning all available interfaces
05 PORT = 8888 # Arbitrary non-privileged port
06
07 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
08 print 'Socket created'
09
10 try:
11 s.bind((HOST, PORT))
12 except socket.error , msg:
13 print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
14 sys.exit()
15
16 print 'Socket bind complete'
17
18 s.listen(10)
19 print 'Socket now listening'
20
21 #wait to accept a connection - blocking call
22 conn, addr = s.accept()
23
24 print 'Connected with ' + addr[0] + ':' + str(addr[1])
25
26 #now keep talking with the client
27 data = conn.recv(1024)
28 conn.sendall(data)
29
30 conn.close()
31 s.close()
继续运行上述代码,然后打开另外一个命令行窗口输入下面命令:

1 $ telnet localhost 8888
2 Trying 127.0.0.1...
3 Connected to localhost.
4 Escape character is '^]'.
5 happy
6 happy
7 Connection closed by foreign host.
可看到客户端接收到来自服务器端的回应内容。

上面的例子还是一样,服务器端回应后就立即退出了。而一些真正的服务器像 www.oschina.net 是一直在运行的,时刻接受连接请求。

也就是说服务器端应该一直处于运行状态,否则就不能成为“服务”,因此我们要让服务器端一直运行,最简单的方法就是把 accept 方法放在一个循环内。

一直在运行的服务器

对上述代码稍作改动:

01 import socket
02 import sys
03
04 HOST = '' # Symbolic name meaning all available interfaces
05 PORT = 8888 # Arbitrary non-privileged port
06
07 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
08 print 'Socket created'
09
10 try:
11 s.bind((HOST, PORT))
12 except socket.error , msg:
13 print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
14 sys.exit()
15
16 print 'Socket bind complete'
17
18 s.listen(10)
19 print 'Socket now listening'
20
21 #now keep talking with the client
22 while 1:
23 #wait to accept a connection - blocking call
24 conn, addr = s.accept()
25 print 'Connected with ' + addr[0] + ':' + str(addr[1])
26
27 data = conn.recv(1024)
28 reply = 'OK...' + data
29 if not data:
30 break
31
32 conn.sendall(reply)
33
34 conn.close()
35 s.close()
很简单只是加多一个 while 1 语句而已。

继续运行服务器,然后打开另外三个命令行窗口。每个窗口都使用 telnet 命令连接到服务器:

1 $ telnet localhost 5000
2 Trying 127.0.0.1...
3 Connected to localhost.
4 Escape character is '^]'.
5 happy
6 OK .. happy
7 Connection closed by foreign host.

服务器所在的终端窗口显示的是:

1 $ python server.py
2 Socket created
3 Socket bind complete
4 Socket now listening
5 Connected with 127.0.0.1:60225
6 Connected with 127.0.0.1:60237
7 Connected with 127.0.0.1:60239

你看服务器再也不退出了,好吧,用 Ctrl+C 关闭服务器,所有的 telnet 终端将会显示 "Connection closed by foreign host."

已经很不错了,但是这样的通讯效率太低了,服务器程序使用循环来接受连接并发送回应,这相当于是一次最多处理一个客户端的请求,而我们要求服务器可同时处理多个请求。

处理多个连接

为了处理多个连接,我们需要一个独立的处理代码在主服务器接收到连接时运行。一种方法是使用线程,服务器接收到连接然后创建一个线程来处理连接收发数据,然后主服务器程序返回去接收新的连接。

下面是我们使用线程来处理连接请求:

01 import socket
02 import sys
03 from thread import *
04
05 HOST = '' # Symbolic name meaning all available interfaces
06 PORT = 8888 # Arbitrary non-privileged port
07
08 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
09 print 'Socket created'
10
11 #Bind socket to local host and port
12 try:
13 s.bind((HOST, PORT))
14 except socket.error , msg:
15 print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
16 sys.exit()
17
18 print 'Socket bind complete'
19
20 #Start listening on socket
21 s.listen(10)
22 print 'Socket now listening'
23
24 #Function for handling connections. This will be used to create threads
25 def clientthread(conn):
26 #Sending message to connected client
27 conn.send('Welcome to the server. Type something and hit enter\n') #send only takes string
28
29 #infinite loop so that function do not terminate and thread do not end.
30 while True:
31
32 #Receiving from client
33 data = conn.recv(1024)
34 reply = 'OK...' + data
35 if not data:
36 break
37
38 conn.sendall(reply)
39
40 #came out of loop
41 conn.close()
42
43 #now keep talking with the client
44 while 1:
45 #wait to accept a connection - blocking call
46 conn, addr = s.accept()
47 print 'Connected with ' + addr[0] + ':' + str(addr[1])
48
49 #start new thread takes 1st argument as a function name to be run, second is the tuple of arguments to the function.
50 start_new_thread(clientthread ,(conn,))
51
52 s.close()
运行上述服务端程序,然后像之前一样打开三个终端窗口并执行 telent 命令:

01 $ telnet localhost 8888
02 Trying 127.0.0.1...
03 Connected to localhost.
04 Escape character is '^]'.
05 Welcome to the server. Type something and hit enter
06 hi
07 OK...hi
08 asd
09 OK...asd
10 cv
11 OK...cv
服务器端所在终端窗口输出信息如下:

1 $ python server.py
2 Socket created
3 Socket bind complete
4 Socket now listening
5 Connected with 127.0.0.1:60730
6 Connected with 127.0.0.1:60731

线程接管了连接并返回相应数据给客户端。

这便是我们所要介绍的服务器端编程。

结论

到这里为止,你已经学习了 Python 的 Socket 基本编程,你可自己动手编写一些例子来强化这些知识。

你可能会遇见一些问题:Bind failed. Error Code : 98 Message Address already in use,碰见这种问题只需要简单更改服务器端口即可。

转:Python 的 Socket 编程教程的更多相关文章

  1. Python:socket编程教程

    ocket是基于C/S架构的,也就是说进行socket网络编程,通常需要编写两个py文件,一个服务端,一个客户端. 首先,导入Python中的socket模块: import socket Pytho ...

  2. Python——Socket 编程教程

    这是用来快速学习 Python Socket 套接字编程的指南和教程.Python 的 Socket 编程跟 C 语言很像. Python 官方关于 Socket 的函数请看 http://docs. ...

  3. 最基础的Python的socket编程入门教程

    最基础的Python的socket编程入门教程 本文介绍使用Python进行Socket网络编程,假设读者已经具备了基本的网络编程知识和Python的基本语法知识,本文中的代码如果没有说明则都是运行在 ...

  4. Python 3 socket 编程

    Python 3 socket编程 一 客户端/服务器架构 互联网中处处是C/S架构 1.C/S结构,即Client/Server(客户端/服务器)结构 2.在互联网中处处可见c/s架构 比如说浏览器 ...

  5. python之socket编程(一)

    socket之前我们先来熟悉回忆几个知识点. OSI七层模型 OSI(Open System Interconnection)参考模型是国际标准化组织(ISO)制定的一个用于计算机或通信系统间互联的标 ...

  6. python学习------socket编程

    一 客户端/服务器架构 1.硬件C/S架构(打印机) 2.软件C/S架构 互联网中处处是C/S架构 如黄色网站是服务端,你的浏览器是客户端(B/S架构也是C/S架构的一种) 腾讯作为服务端为你提供视频 ...

  7. Python基础socket编程

    Python 提供了两个基本的 socket 模块. 第一个是 Socket,它提供了标准的 BSD Sockets API. 第二个是 SocketServer, 它提供了服务器中心类,可以简化网络 ...

  8. Python基础-socket编程

    一.网络编程 自从互联网诞生以来,现在基本上所有的程序都是网络程序,很少有单机版的程序了. 计算机网络就是把各个计算机连接到一起,让网络中的计算机可以互相通信.网络编程就是如何在程序中实现两台计算机的 ...

  9. Python菜鸟之路:Python基础-Socket编程-2

    在上节socket编程中,我们介绍了一些TCP/IP方面的必备知识,以及如何通过Python实现一个简单的socket服务端和客户端,并用它来解决“粘包”的问题.本章介绍网络编程中的几个概念:多线程. ...

随机推荐

  1. Nobel Lecture, December 12, 1929 Thermionic phenomena and the laws which govern them

    http://www.nobelprize.org/nobel_prizes/physics/laureates/1928/richardson-lecture.pdf OWEN W. RICHARD ...

  2. 不遗留问题-menu数据拼装

    DROP TABLE IF EXISTS `menu0910`; CREATE TABLE `menu0910` ( `id` ) NOT NULL AUTO_INCREMENT, `menu` ) ...

  3. JavaScript中的prototype

    关于prototype: 理解prototype不应把它和继承混淆.A的prototype为B的一个实例,可以理解A将B中的方法和属性全部克隆了一遍.A能使用B的方法和属性.这里强调的是克隆而不是继承 ...

  4. ASP.NET WebForm与ASP.NET MVC的不同点

    ASP.NET WebForm ASP.NET MVC ASP.NET Web Form 遵循传统的事件驱动开发模型 ASP.NET MVC是轻量级的遵循MVC模式的请求处理响应的基本开发模型 ASP ...

  5. Bluetooth HCI介绍

    目录 1. HCI功能 2. HCI Packet 1. HCI Command 2. HCI Event 3. HCI Data 3. HCI传输层 HCI, 主机控制接口(Host Control ...

  6. 自动将每日的日志增量导入到hive中

    一:大纲介绍 1.导入方式 load data local inpath 'local_file_path' into table tbname partition (date='',hour='') ...

  7. php之curl实现http与https请求的方法

    原文地址:http://m.jb51.net/show/56492   这篇文章主要介绍了php之curl实现http与https请求的方法,分别讲述了PHP访问http网页与访问https网页的实例 ...

  8. php——n维数组的遍历——递归

    <?php /**** ****/function digui($arr){    foreach($arr as $key => $value)    {        if(is_ar ...

  9. ArcGIS Engine开发之旅08--和查询相关的对象和接口

    原文:ArcGIS Engine开发之旅08--和查询相关的对象和接口 查询在GIS领域应该是一个很频繁的操作,在GIS中除了具有属性查询(和其他关系型数据库的查询类似),还提供了空间查询.在介绍查询 ...

  10. ecshop前台英文后台中文

    方法一:修改init.php文件方法改变ecshop默认语言包 该方法适用于:安装ecshop中文版的站长使用. 1.进入您的后台,按照下图提示,将系统语言选项设为 en_us(英语) ,确定保存. ...