Socket
网络上两个程序通过socket接口进行通信,一个是服务器端,一个是客户端
socket则是对TCP/IP协议的封装和应用(程序员层面上)。
Socket本身并不是协议,而是一个调用接口(API)。
通过Socket,我们才能使用TCP/IP协议。
Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,
TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口
创建一个socket服务端:
客户端编程
- 创建 Socket
- 连接到远程服务器
- 发送数据
- 接收回应
创建socket
#Socket client example in python
import socket #for sockets
#create an AF_INET, STREAM socket (TCP)
#返回 Socket 的描述符可用于其他 Socket 相关的函数
#地址簇 : AF_INET (IPv4)
#类型: SOCK_STREAM (使用 TCP 传输控制协议)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket Created'
错误处理
#handling errors in python socket programs
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'
注意
与 SOCK_STREAM 相对应的其他类型是 SOCK_DGRAM 用于 UDP 通讯协议,UDP 通讯是非连接 Socket,在这篇文章中我们只讨论 `SOCK_STREAM ,或者叫 TCP 。
2. 连接到服务器
连接到服务器需要服务器地址和端口号
首先获取远程主机的 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.baidu.com'
try:
remote_ip = socket.gethostbyname( host )#获取远程主机的ip地址
except socket.gaierror:
#could not resolve
print 'Hostname could not be resolved. Exiting'
sys.exit()
print 'Ip address of ' + host + ' is ' + remote_ip
gethostbyname()–>用域名或主机名获取IP地址
已经有 IP 地址了,接下来需要 指定要连接的端口
创建了一个 Socket 并进行连接
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.baidu.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
已经连接上了,接下来就是往服务器上 发送数据
。
使用 SOCK_STREAM/TCP 套接字才有“连接”的概念
UDP、ICMP 和 ARP 没有“连接”的概念
发送数据
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.oschina.net'
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_ipimport 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.oschina.net'
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
#send sone data to remote server
message = "GET / HTTP/1.1\r\n\r\n"
try:
#set the whole string
#向远程服务器发送字符串数据 "GET / HTTP/1.1\r\n\r\n" ,这是一个 HTTP 协议的命令,用来获取网站首页的内容。
s.sendall(message)
except socket.error:
#send failed
print 'send failed'
sys.exit()
print 'Message send successfully'
接下来需要读取服务器返回的数据。
接收数据
recv 函数用于从
socket 接收数据
#Socket client example in python
import socket #for sockets
import sys #for exit
#create an INET, STREAMing socket
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error:
print 'Failed to create socket'
sys.exit()
print 'Socket Created'
host = 'oschina.net';
port = 80;
try:
remote_ip = socket.gethostbyname( host )
except socket.gaierror:
#could not resolve
print 'Hostname could not be resolved. Exiting'
sys.exit()
#Connect to remote server
s.connect((remote_ip , port))
print 'Socket Connected to ' + host + ' on ip ' + remote_ip
#Send some data to remote server
message = "GET / HTTP/1.1\r\nHost: oschina.net\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'
#now receive data
#从socket中接收服务器返回的数据
reply = s.recv(4096)
print reply
运行结果:
回应了我们所请求的 URL 的内容,很简单。数据接收完了,可以关闭 Socket 了
关闭 socket
s.close()
服务器端编程
- 打开 socket
- 绑定到一个地址和端口
- 侦听进来的连接
- 接受连接
- 读写数据
打开Socket
import socket
import sys
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'
绑定socket
bind()将 Socket 绑定到一个特定的地址和端口(自己写的服务器)
需要一个类似 connect 函数所需的 sockaddr_in 结构体。
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'
绑定完成后,就需要让 Socket 开始侦听连接。
很显然,你不能将两个不同的 Socket 绑定到同一个端口之上。
连接侦听
需要将 Socket 变成侦听模式
socket 的 listen 函数用于实现侦听模式:
参数:backlog,用来控制程序忙时可保持等待状态的连接数
s.listen(10)
print 'Socket now listening'
接受连接
s.accept()
–>
接受TCP连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。
s.accept()–>接收客户端的请求
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 connectiong - blocking call
#等待接收连接
#conn是新的套接字对象,可以用来接收和发送数据。
#address是连接客户端的地址。
conn,addr = s.accept()
#display client information
#展示客户端信息
print 'connected with' + addr[0] + ':' + str(addr[1])
让我们来给客户端做出点回应
sendall 函数可通过 Socket 给客户端发送数据
一直在运行的服务器
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'
while 1:
#wait to accept a connection - blocking call
conn, addr = s.accept()#建立连接,并返回1 新的套接字2.客户端地址
print 'Connected with ' + addr[0] + ':' + str(addr[1])
#对请求进行处理
# now keep talking with the client
#把接收的数据实例化
data = conn.recv(1024)#用新的套接字接收数据,并实例化
#结果发给对端(即客户端)
conn.sendall(data)#sendall 函数可通过 Socket 给客户端发送数据
conn.close()
s.close()
运行结果:
可看到客户端已经成功连接到服务器
处理多个连接请求
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):
#send message to connected client
##send only takes string
#发送到客户端
conn.send("welcome to the server.type something and hit enter\n")
#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
#向客户端发送“OK...”,客户端中显示OK表明发送成功
conn.sendall(reply)
#came out of loop
conn.close()
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,))#进入clientthread执行
s.close()
start_new_thread
( function , args [ , kwargs ] )
创建一个新的线程,返回一个线程标识符。
function是线程函数,
args是线程函数的参数,是一个list。
kwargs可选参数,可不填。
线程接管了连接并返回相应数据给客户端。
这便是我们所要介绍的服务器端编程。
================================================================================
socket编程思路
TCP服务端:
1. 创建套接字,绑定套接字到本地IP与端口
# socket.socket(socket.AF_INET,socket.SOCK_STREAM) , s.bind()
2. 开始监听连接 #s.listen()
3. 进入循环,不断接受客户端的连接请求 #s.accept()
4. 然后接收传来的数据,并发送给对方数据 #s.recv() , s.sendall()
5. 传输完毕后,关闭套接字 #s.close()
Socket编程之服务端代码:
import socket #socket模块
import commands #执行系统命令模块
HOST='10.0.0.245'
PORT=50007
s= socket.socket(socket.AF_INET,socket.SOCK_STREAM) #定义socket类型,网络通信,TCP
s.bind((HOST,PORT)) #套接字绑定的IP与端口
s.listen(1) #开始TCP监听
while 1:
conn,addr=s.accept() #接受TCP连接,并返回新的套接字与IP地址
print'Connected by',addr #输出客户端的IP地址
while 1:
data=conn.recv(1024) #把接收的数据实例化
cmd_status,cmd_result=commands.getstatusoutput(data) #commands.getstatusoutput执行系统命令(即shell命令),返回两个结果,第一个是状态,成功则为0,第二个是执行成功或失败的输出信息
if len(cmd_result.strip()) ==0: #如果输出结果长度为0,则告诉客户端完成。此用法针对于创建文件或目录,创建成功不会有输出信息
conn.sendall('Done.')
else:
conn.sendall(cmd_result) #否则就把结果发给对端(即客户端)
conn.close() #关闭连接
Socket编程之客户端代码:
import socket
HOST='10.0.0.245'
PORT=50007
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #定义socket类型,网络通信,TCP
s.connect((HOST,PORT)) #要连接的IP与端口
while 1:
cmd=raw_input("Please input cmd:") #与人交互,输入命令
s.sendall(cmd) #把命令发送给对端
data=s.recv(1024) #把接收的数据定义为变量
print data #输出变量
s.close() #关闭连接
参考:
https://www.oschina.net/question/12_76126
http://yangrong.blog.51cto.com/6945369/1339593