Python宝典第18章:Python网络编程

Python的标准模块中,既有socket进行底层网络编程,还有各种针对特定网络协议进行编程的模块。

除了标准模块,还可以使用Twisted进行网络编程。

使用socket模块:

socket模块提供了底层的网络接口,可以实现网络上不同计算机之间的通信。

Python的socket实现了BSD套接字标准。

socket是网络连接端点。每个socket都被绑定到指定IP和端口上。

使用socket创建的通信应该有服务端和客户端,服务端首先创建一个socket,并等待客户端的连接。

客户端建立与服务端的socket连接,当连接成功后,客户端和服务端就可以使用socket进行通信了。

socket(family, type, proto)

bind(address)

listen(backlog)

建立服务端:(以下为传送一句话从客户端到服务端,中英文皆可)

# -*-coding:utf-8 -*-
# file: server.py
#

import tkinter
import threading
import socket

class ListenThread(threading.Thread): #监听线程
    def __init__(self, edit, server):
        threading.Thread.__init__(self)
        self.edit=edit #保存窗口中的多行文本框
        self.server=server
    def run(self): #进入监听状态
        while 1:
            try:
                client, addr=self.server.accept() #等待连接
                self.edit.insert(tkinter.END, "连接来自:%s:%d\n" % addr)
                data=client.recv(1024) #接收数据
                self.edit.insert(tkinter.END, "收到数据:%s \n" % data.decode('utf-8'))
                client.send(data) #发送数据
                client.close()
                self.edit.insert(tkinter.END,"关闭客户端!\n")
            except:
                self.edit.insert(tkinter.END, "关闭连接\n")
                break #结束循环

class Control(threading.Thread): #控制线程
    def __init__(self, edit):
        threading.Thread.__init__(self)
        self.edit=edit
        self.event=threading.Event() #创建Event对象
        self.event.clear() #清除event标志
    def run(self):
        server=socket.socket(socket.AF_INET, socket.SOCK_STREAM) #创建socket连接
        server.bind(('', 1051)) #绑定端口
        server.listen(1) #开始监听
        self.edit.insert(tkinter.END, '正在等待连接...\n')
        self.lt=ListenThread(self.edit,server) #创建监听线程对象
        self.lt.setDaemon(True)
        self.lt.start() #执行监听线程
        self.event.wait() #进入等待状态
        server.close
    def stop(self):
        self.event.set()        

class Window:
    def __init__(self, root):
        self.root=root
        self.butlisten=tkinter.Button(root, text='开始监听', command=self.Listen)
        self.butlisten.place(x=20,y=15)
        self.butclose=tkinter.Button(root, text='停止监听', command=self.Close)
        self.butclose.place(x=120,y=15)
        self.edit=tkinter.Text(root)
        self.edit.place(y=50)
    def Listen(self): #按钮事件
        self.ctrl=Control(self.edit) #创建控制线程对象
        self.ctrl.setDaemon(True) 
        self.ctrl.start() #执行控制线程
    def Close(self):
        self.ctrl.stop()

root=tkinter.Tk()
window=Window(root)
root.mainloop()

客户端:

# -*-coding:utf-8 -*-
# file: client.py
#

import tkinter
import socket

class Window:
    def __init__(self, root):
        label1=tkinter.Label(root, text='IP')
        label1.place(x=5, y=5)
        label2=tkinter.Label(root, text='Port')
        label2.place(x=5, y=30)
        label3=tkinter.Label(root, text='Data')
        label3.place(x=5, y=55)
        self.entryIP=tkinter.Entry(root)
        self.entryIP.insert(tkinter.END, '127.0.0.1')
        self.entryIP.place(x=40, y=5)
        self.entryPort=tkinter.Entry(root)
        self.entryPort.insert(tkinter.END, '1051')
        self.entryPort.place(x=40, y=30)
        self.entryData=tkinter.Entry(root)
        self.entryData.insert(tkinter.END, 'Hello')
        self.entryData.place(x=40, y=55)
        self.Recv=tkinter.Text(root)
        self.Recv.place(y=115)
        self.send=tkinter.Button(root, text='发送数据', command=self.Send)
        self.send.place(x=40, y=80)
    def Send(self): #按钮事件
        try:
            ip=self.entryIP.get() #获得IP
            port=int(self.entryPort.get())
            data=self.entryData.get()
            client=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            client.connect((ip,port))
            client.send(data.encode('utf-8'))
            rdata=client.recv(1024)            
            self.Recv.insert(tkinter.END, '服务器返回: %s \n' % rdata.decode('utf-8'))
            client.close()
        except:
            self.Recv.insert(tkinter.END, '发送错误\n')

root=tkinter.Tk()
window=Window(root)
root.mainloop()          


在局域网中传输文件:(上面的例子稍作修改,文件内容可以含中文,但是路径和文件名不能含中文)

服务端:

# -*-coding:utf-8 -*-
# file: fileserver.py
#

import tkinter
import threading
import socket
import os

class ListenThread(threading.Thread): 
    def __init__(self, edit, server):
        threading.Thread.__init__(self)
        self.edit=edit 
        self.server=server
        self.file='receive.txt'
    def run(self): 
        while 1:
            try:
                self.client, addr=self.server.accept() #?
                self.edit.insert(tkinter.END, "连接来自:%s:%d\n" % addr)
                self.edit.insert(tkinter.END, "开始接收数据:")
                file=os.open(self.file, os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_BINARY)
                while 1:
                    rdata=self.client.recv(1024)
                    if not rdata:
                        break
                    os.write(file,rdata)
                    self.edit.insert(tkinter.END, "......")
                os.close(file)                
                self.client.close()
                self.edit.insert(tkinter.END,"\n接收完毕,关闭客户端!\n")
            except:
                self.edit.insert(tkinter.END, "\n网络错误,关闭连接\n")
                break 

class Control(threading.Thread): 
    def __init__(self, edit):
        threading.Thread.__init__(self)
        self.edit=edit
        self.event=threading.Event() 
        self.event.clear() 
    def run(self):
        server=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.bind(('', 1051)) 
        server.listen(1) 
        self.edit.insert(tkinter.END, '正在等待连接...\n')
        self.lt=ListenThread(self.edit,server) 
        self.lt.setDaemon(True)
        self.lt.start() 
        self.event.wait() 
        server.close
    def stop(self):
        self.event.set()        

class Window:
    def __init__(self, root):
        self.root=root
        self.butlisten=tkinter.Button(root, text='开始监听', command=self.Listen)
        self.butlisten.place(x=20,y=15)
        self.butclose=tkinter.Button(root, text='停止监听', command=self.Close)
        self.butclose.place(x=120,y=15)
        self.edit=tkinter.Text(root)
        self.edit.place(y=50)
    def Listen(self): 
        self.ctrl=Control(self.edit) 
        self.ctrl.setDaemon(True) 
        self.ctrl.start() 
    def Close(self):
        self.ctrl.stop()

root=tkinter.Tk()
window=Window(root)
root.mainloop()

客户端:

# -*-coding:utf-8 -*-
# file: fileclient.py
#

import tkinter
import socket
import tkinter.filedialog
import os

class Window:
    def __init__(self, root):
        label1=tkinter.Label(root, text='IP')
        label1.place(x=5, y=5)
        label2=tkinter.Label(root, text='Port')
        label2.place(x=5, y=30)
        label3=tkinter.Label(root, text='文件')
        label3.place(x=5, y=55)
        self.entryIP=tkinter.Entry(root)
        self.entryIP.insert(tkinter.END, '127.0.0.1')
        self.entryIP.place(x=40, y=5)
        self.entryPort=tkinter.Entry(root)
        self.entryPort.insert(tkinter.END, '1051')
        self.entryPort.place(x=40, y=30)
        self.entryData=tkinter.Entry(root)
        self.entryData.insert(tkinter.END, 'Hello')
        self.entryData.place(x=40, y=55)
        self.openfile=tkinter.Button(root, text='浏览', command=self.Openfile)
        self.openfile.place(x=170,y=55)
        self.send=tkinter.Button(root, text='发送文件', command=self.Send)
        self.send.place(x=40, y=80)
    def Send(self): 
        try:
            ip=self.entryIP.get() 
            port=int(self.entryPort.get())
            filename=self.entryData.get()
            tt=filename.split('/')
            name=tt[len(tt)-1]
            client=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            client.connect((ip,port))
            client.send(name.encode('utf-8'))
            file=os.open(filename, os.O_RDONLY|os.O_EXCL|os.O_BINARY)
            while 1:
                data=os.read(file,1024)
                if not data:
                    break
                client.send(data)
            os.close(file)
            client.close()
        except Exception as e:
            print('发送错误',e)
    def Openfile(self):
        r=tkinter.filedialog.askopenfilename(title='Python tkinter',\
                                             filetypes=[('All files', '*'),\
                                                        ('Python','*.py *.pyc')])
        if r:
            self.entryData.delete(0,tkinter.END)#0 can clear it
            self.entryData.insert(tkinter.END,r)

root=tkinter.Tk()
window=Window(root)
root.mainloop()



使用urllib,httplib和ftplib:

Python提供的socket模块主要用于底层网络协议,编写程序较为麻烦。

针对常用的HTTP和FTP协议,可以用httplib和ftplib进行访问。

urllib.request模块提供urlopen,urlretrieve方法。

urllib.parse模块


httplib.client模块:

提供HTTPConnection对象和HTTPResponse对象。

创建一个HTTPConnection对象后,可以使用request方法向服务器发送请求。

request(method, url, body, headers)

发送完请求后,可以使用HTTPConnection中的getresponse方法返回一个HTTPResponse对象。Close方法关闭与服务器的连接。

HTTPResponse对象用于处理服务器对所发送请求的回应。


urllib模块可以创建一个简单访问网站的脚本,获取指定页面。使用httplib也可以但是过程复杂,适用于需要用户名和密码认证的网站。而urllib只能简单访问下载页面内容。

# -*-coding: utf-8 -*-
# file: httpurl.py
#

import tkinter
import urllib.request

class Window:
    def __init__(self, root):
        self.root=root
        self.entryUrl=tkinter.Entry(root)
        self.entryUrl.place(x=5,y=15)
        self.get=tkinter.Button(root, text="下载页面", command=self.Get)
        self.get.place(x=160, y=12)
        self.edit=tkinter.Text(root)
        self.edit.place(x=5, y=50)
    def Get(self):
        url=self.entryUrl.get()
        page=urllib.request.urlopen(url)
        data=page.read()
        self.edit.insert(tkinter.END, data)
        page.close()

root=tkinter.Tk()
window=Window(root)
root.minsize(580,380)
root.mainloop

访问FTP:

使用ftplib模块中的FTP类,可以创建FTP连接对象

FTP(host,user,passwd,acct)

建立FTP对象时未指定host,则可以用connect方法连接。同样有login方法。

  • getwelcome:获得服务器欢迎信息
  • abort:中断文件传输
  • sendcmd、voidcmd:向服务器发送指令,voidcmd没有返回值
  • retrbinary、retrlines:下载文件,一个二进制文件,一个ASCII形式
  • storbinary、storlines:上传文件
  • dir:获取目录内容列表
  • rename:改名
  • delete:删除文件
  • cwd:改变当前目录
  • mkd:创建目录
  • pwd:获得当前目录
  • rmd:删除目录
  • size:获得文件大小
  • quit、close关闭与服务器的连接
# -*-coding: utf-8 -*-
# file: pyftp.py
#

from ftplib import FTP
bufsize=1024

def Get(filename):
    command='RETR '+filename
    ftp.retrbinary(command, open(filename, 'wb').write, bufsize)
    print("下载成功")

def Put(filename):
    command ='STOR '+filename
    filehander=open(filename, 'rb')
    ftp.storbinary(command, filehandler, bufsize)
    filehander.close()
    print("上传成功")

def PWD():
    print(ftp.pwd())

def Size(filename):
    print(ftp.size(filename))

def Help():
    print('''
    =================================
          Simple Python FTP
    =================================
    cd 进入文件夹
    delete 删除文件
    dir 获取当前文件列表
    get 下载文件
    help 帮助
    mkdir 创建文件夹
    put 上传文件
    pwd 获取当前目录
    renmae 重命名文件
    rmdir 删除文件夹
    size 获取文件大小
    ''')

server=input("请输入FTP服务器地址:")
ftp=FTP(server)
username=input("请输入用户名:")
password=input("请输入密码:")
ftp.login(username, password)
print (ftp.getwelcome())
actions={'dir':ftp.dir, 'pwd':PWD, 'cd':ftp.cwd, 'get':Get,\
         'put':Put, 'help':Help, 'rmdir': frp.rmd,\
         'mkdir': ftp.mkd, 'delete':ftp.delete,\
         'size':Size, 'rename':ftp.rename}
while true:
    print('pyftp>', )
    cmds=input()
    cmd=str.split(cmds)
    try:
        if len(cmd)==1:
            if str.lower(cmd[0])=='quit':
                break
            else:
                actions[str.lower(cmd[0])]()
        elif (len(cmd))==2:
            actions[str.lower(cmd[0])](cmd[1])
        elif (len(cmd))==3:
            actions[str.lower(cmd[0])](cmd[1], cmd[2])
        else:
              print("输入错误!")
    except:
        print("命令出错!")
ftp.quit()

Python中的poplib模块和smtplib模块支持POP3协议和SMTP协议。前者可以登录邮箱服务器收取邮件,后者可以发送邮件。
  • POP3(host, port)
  • user(username)
  • pass_(password)
  • stat:获取邮件状态
  • list:获取邮件列表
  • retr:获取指定邮件
  • dele
  • top(which, howmuch)
  • rset:清除收件箱邮件的删除标记
  • noop:保持与服务器连接
  • quit:断开与服务器的连接
收取邮件:邮件有中文是乱码,暂时没时间折腾编码这种Python初学者常见问题。。。如果以后工作遇到就集中攻关下。。。
# -*-coding: utf-8 -*-
# file: pypop3.py
#


import poplib
import re
import tkinter


class Window:
    def __init__(self, root):
        label1=tkinter.Label(root, text='POP3')
        label2=tkinter.Label(root, text='Port')
        label3=tkinter.Label(root, text='用户名')
        label4=tkinter.Label(root, text='密码')
        label1.place(x=5,y=5)
        label2.place(x=5,y=30)
        label3.place(x=5,y=55)
        label4.place(x=5,y=80)
        self.entryPOP=tkinter.Entry(root)        
        self.entryPort=tkinter.Entry(root)
        self.entryUser=tkinter.Entry(root)
        self.entryPass=tkinter.Entry(root, show='*')
        self.entryPOP.insert(tkinter.END,"pop.163.com")
        self.entryPort.insert(tkinter.END,"110")
        self.entryUser.insert(tkinter.END,"[email protected]")
        self.entryPass.insert(tkinter.END,"lydd22208")
        self.entryPOP.place(x=50, y=5)
        self.entryPort.place(x=50, y=30)
        self.entryUser.place(x=50, y=55)
        self.entryPass.place(x=50, y=80)
        self.get=tkinter.Button(root, text='收取邮件',command=self.Get)
        self.get.place(x=60, y=120)
        self.text=tkinter.Text(root)
        self.text.place(y=150)
    def Get(self):
        try:
            host=self.entryPOP.get()
            port=int(self.entryPort.get())
            user=self.entryUser.get()
            pw=self.entryPass.get()
            pop=poplib.POP3(host)
            pop.user(user)
            pop.pass_(pw)
            stat=pop.stat()
            #print(stat)
            self.text.insert(tkinter.END, 'Status: %d message(s), %d bytes\n' % stat)
            rx_headers=re.compile(r"^(From|To|Subject)")
            for n in range(stat[0]):
                #print(pop.top(n+1, 10))
                response, lines, bytes=pop.top(n+1, 10)
                #print(lines)
                self.text.insert(tkinter.END, "Message %d (%d bytes)\n" % (n+1, bytes))
                self.text.insert(tkinter.END, "-"*30+'\n')
                str_lines=[]
                for l in lines:
                    str_lines.append(l.decode('utf-8', 'ignore'))
                #str_lines.reverse()
                self.text.insert(tkinter.END, "\n".join(filter(rx_headers.match, str_lines)))
                self.text.insert(tkinter.END, '\n')
                self.text.insert(tkinter.END, "-"*30+'\n')
        except Exception as e:
            self.text.insert(tkinter.END, "接受错误\n")


root=tkinter.Tk()
window=Window(root)
root.mainloop()    

发送邮件:
使用Python的smtplib模块和SMTP协议。smtplib模块简介:
  • SMTP(host, port, local_hostname)
  • lconnect
  • login
  • set_debuglevel
  • docmd(cmd,, argstring)
  • sendmail(from_addr, to_address, msg, mail_options, rcpt_options)
  • quit
同上,这里仍然不能发送中文邮件。
# -*-coding: utf-8 -*-
# file: pysmtp.py
#

import smtplib
import tkinter

class Window:
    def __init__(self, root):
        label1=tkinter.Label(root, text='SMTP')
        label2=tkinter.Label(root, text='Port')
        label3=tkinter.Label(root, text='用户名')
        label4=tkinter.Label(root, text='密码')
        label5=tkinter.Label(root, text='收件人')
        label6=tkinter.Label(root, text='主题')
        label7=tkinter.Label(root, text='发件人')
        label1.place(x=5,y=5)
        label2.place(x=5,y=30)
        label3.place(x=5,y=55)
        label4.place(x=5,y=80)
        label5.place(x=5,y=105)
        label6.place(x=5,y=130)
        label7.place(x=5,y=155)
        self.entrySMTP=tkinter.Entry(root)        
        self.entryPort=tkinter.Entry(root)
        self.entryUser=tkinter.Entry(root)
        self.entryPass=tkinter.Entry(root, show='*')
        self.entryTo=tkinter.Entry(root)        
        self.entrySub=tkinter.Entry(root)
        self.entryFrom=tkinter.Entry(root)
        self.entrySMTP.insert(tkinter.END,"smtp.163.com")
        self.entryPort.insert(tkinter.END,"25")        
        self.entryUser.insert(tkinter.END,"[email protected]")
        self.entryPass.insert(tkinter.END,"lydd22208")
        self.entryTo.insert(tkinter.END,"[email protected]")        
        self.entrySub.insert(tkinter.END,"test")
        self.entryFrom.insert(tkinter.END,"[email protected]")
        self.entrySMTP.place(x=50, y=5)        
        self.entryPort.place(x=50, y=30)
        self.entryUser.place(x=50, y=55)
        self.entryPass.place(x=50, y=80)
        self.entryTo.place(x=50, y=105)
        self.entrySub.place(x=50, y=130)
        self.entryFrom.place(x=50, y=155)
        self.get=tkinter.Button(root, text='发送邮件',command=self.Send)
        self.get.place(x=60, y=180)
        self.text=tkinter.Text(root)
        self.text.place(y=220)
        self.text.insert(tkinter.END,'This is just a test!')
    def Send(self):
        try:
            host=self.entrySMTP.get()
            port=int(self.entryPort.get())
            user=self.entryUser.get()
            pw=self.entryPass.get()
            fromaddr=self.entryFrom.get()
            toaddr=self.entryTo.get()
            subject=self.entrySub.get()
            text=self.text.get(1.0, tkinter.END)
            msg=("From: %s\nTo: %s\nSubject: %s\n\n" % (fromaddr, toaddr, subject))
            msg=msg+text
            smtp=smtplib.SMTP(host, port)
            smtp.set_debuglevel(1)
            smtp.login(user,pw)
            smtp.sendmail(fromaddr,toaddr,msg)
            smtp.quit()
        except Exception as e:
            self.text.insert(tkinter.END, "\n发送错误\n")

root=tkinter.Tk()
window=Window(root)
root.minsize(600,480)
root.mainloop()    


你可能感兴趣的:(Pyhton学习笔记)