知乎:https://www.zhihu.com/people/monkey.d.luffy Android高级开发交流群2: 752871516
全部博文(315)
分类: Python/Ruby
2012-02-22 21:42:43
网络
关于核心编程的第二部分的网络相关的东西,还是蛮有用的,和以前学习c的时候差不多,很多函数都类似甚至相同,虽然文章将的不细,但对于网络的学习还是有帮助的。多了解点是点,多学点是点…
一》下面就是文章的例子:(至于关于tcp, udp,套接字等概念,越清晰越好)
TCP服务器端:tsTserv.py
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from socket import *
from time import ctime
if __name__ == "__main__":
HOST = '' #表示可以接受所有有效地址
PORT = 21567
BUFSIZE = 1024 #数据缓冲区
ADDR = (HOST, PORT)
tcpSerSock = socket(AF_INET, SOCK_STREAM) #1>创建一个网络流套接字
tcpSerSock.bind(ADDR) #2>绑定
tcpSerSock.listen(10) #3>可以监听10个
while True: #利用进程来做是最好的!
print 'Waitting for connection...'
tcpCliSock, addr = tcpSerSock.accept() #4>阻塞等待接受,返回对方地址和一个新的套接字对象,就是以元组的方式存储起来,这里我们分别赋值给tcpCliSock, addr, 在udp中我们就用一个元组对象来存储,然后同过下标值访问即可
print '...connected from:', addr
while True:
data = tcpCliSock.recv(BUFSIZE) #5>获取对方发送的数据,一般为请求的服务,并存储在缓冲区中
if not data: #没有数据就等待下次连接
break
tcpCliSock.send('[%s] %s' % (ctime(), data)) #6>将接受的数据加上时间应答给客户,收发都可以自己定
tcpCliSock.close() #7>注意完成客户/服务器操作要关闭,否则会占有大量的网络资源;但是如果用户还没关闭,就不能关闭,但是对于一般用户请求基本就进行一次交互就关闭,避免占有资源!
break #为了完成一次交互,我停止循环,不如服务器无法接受更多请求,和上句一起放在内层while之外就可以无限交互
tcpSerSock.close() #最后关闭监听的服务器
TCP客户端: tsTlnt.py
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from socket import *
if __name__ == "__main__":
HOST = 'localhost' #本地主机名
PORT = 21567 #服务器端口
BUFSIZE = 1024 #缓冲区一般是4的整数倍
ADDR = (HOST, PORT)
tcpCliSock = socket(AF_INET, SOCK_STREAM) #1>套接字创建, 类型和服务器一样
tcpCliSock.connect(ADDR) #2>建立连接
while True:
data = raw_input('>')
if not data:
break
tcpCliSock.send(data) #3>发送数据,一般应用中发送请求,服务器再去应答你的服务
data = tcpCliSock.recv(BUFSIZE) #4>接受服务器返回的数据,在实际应用中,收发数据的数据说不准了,而且可能会有很多层嵌套,尤其在做文件服务器的时候,为了区分不同的命令,你需要多次进行客户/服务器的应答交互
#print '---data--', data #到这里出错了,没数据,发现是我的服务器每次接受完一个连接,收发完一次数据后,又等待连接出错的,一旦连接上这一次就不该断开
if not data:
break
print data
tcpCliSock.close() #5>同样记住,完成交互后记得关闭
break #就一次交互,当然将上句和这句提到while之外就可以无限交互了,这样太死了;看了得用进程来做了
UDP服务器端:tsUserv.py
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from socket import *
from time import ctime
if __name__ == "__main__":
HOST = ''
PORT = 21567
BUFSIZE = 1024
ADDR = (HOST, PORT)
udpSerSock = socket(AF_INET, SOCK_DGRAM)
udpSerSock.bind(ADDR) #完了后,没有设置像tcp的最大连接数
try:
while True: #在udp中不需要像tcp的accept()这样的操作,因为我们一种类似广播这样的通信机制,是无连接的,不需要很高的可靠性;同时为了减少三次握手的时间消耗
print 'Waitting for message...'
#data, addr = udpSerSock.recvfrom(BUFSIZE) #可以被元组替代,不过还是这种写法清晰,推荐这种
#udpSerSock.sendto('[%s] %s' % (ctime(), data), addr) #拼接了时间和数据,并且发送
data_addr = udpSerSock.recvfrom(BUFSIZE)
udpSerSock.sendto('[%s] %s' % (ctime(), data_addr[0]), data_addr[1]) #拼接了时间和数据,并且发送
print '...received from and returned to: ', data_addr[1]
except KeyboardInterrupt:
print 'bye.....'
udpSerSock.close() #服务器还是让它一直运行,这个关闭应该放在异常捕捉里面(比如键盘中断KeyboardInterrrupt)
UDP客户端: tsUclnt.py
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from socket import *
if __name__ == "__main__":
HOST = 'localhost' #本机地址
PORT = 21567
BUFSIZE = 1024
ADDR = (HOST, PORT)
udpCliSock = socket(AF_INET, SOCK_DGRAM)
while True:
data = raw_input('>')
if not data:
break
udpCliSock.sendto(data, ADDR) #客户端就发送数据,然后接收返回数据
data, ADDR = udpCliSock.recvfrom(BUFSIZE)
if not data:
break
print data
udpCliSock.close()
后面还有利用python封装好的服务器类来做自己的服务器,主要就是自己实现handler接口,然后传入类中即可,可以大大减少代码量的编写,不过另一方面也让人变懒了..呵呵
最后有个关于Twisted框架,就是一个事件驱动的网络框架,我的理解就是一个代码框架,我们导入它,去重新它的方法,来达到我们的要求,而这个框架就是一个模块似的。
# file: twisted.py
#!/usr/bin/env python
from twisted.internet import protocol, reactor
from time import ctime
PORT = 21567
class TSServProtocol(protocol.Protocol):
def connectionMade(self):
clnt = self.clnt = self.transport.getPeer().host #通过类对象来得到本地主机名
print '...connected from:', clnt
def dataReceived(self, data): #重写数据接收函数
self.transport.write('[%s] %s' % (ctime(), data))
factory = protocol.Factory() #工厂:每连接一个就会创建一个这样的实例
factory.protocol = TSServProtocol #利用twisted框架做的自己的服务器
print 'waiting for connection...'
reactor.listenTCP(PORT, factory)
reactor.run()
我们有必要去看下以前练习的C的tcp的编程,以此来比较下有何不同;(其实大同小异了python似乎基本上是c实现的,还有类的话,编译器估计还有些其他lg,有必要了解下,光顾着学它去了)
//file: main.c //这是c的主函数,其他函数都在相应文件,这是服务器端的主要流程,一样的,不过c需要处理网络字节流的问题;其实python也有相关的处理函数,看了下,和c的差不多
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "service.h"
#include "common.h"
int general_flag; //just for return value test
int general_n; //just for return bytes test
//--------------------------------------------------------------------
// Function prototype
//------------------------------------------------------------------
void sigchild_handler(int sig);
int main(int argc, const char *argv[])
{
unsigned short port;
int backlog;
port = (unsigned short )strtol("5000", NULL, 10);
backlog = (unsigned short )strtol("10", NULL, 10);
//define buffer
char buffer[BUFFER_SIZE];
memset(buffer, 0, sizeof(buffer)); //*******remember this
//-----------------------------------------------------------------------
//create socket
//-----------------------------------------------------------------------
//1 step: create socket
//int socket(int domain, int type, int protocol);
int listening_socket;
if((listening_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
fprintf(stdout, "socket failed %s\n", strerror(errno));
exit(1);
}
fprintf(stdout, "Create socket success, listning = %d \n", listening_socket);
int optval;
optval = 1;
//-----------------------------------------------------------------------
//setsockopt
//-----------------------------------------------------------------------
//int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);
if(setsockopt(listening_socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0)
{
fprintf(stderr, "Set SO_REUSEADDR on liste ning_socket failed: %s\n", strerror(errno));
}
fprintf(stderr, "Set SO_REUSEADDR on listeni ng_socket successfully.\n");
#ifdef SO_REUSEPORT
optval = 1;
if(setsockopt(listening_socket, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) < 0)
{
fprintf(stderr, "Set SO_REUSEPORT on liste ning_socket failed: %s\n", strerror(errno));
}
fprintf(stderr, "Set SO_REUSEPORT on listeni ng_socket successfully.\n");
#endif
//-----------------------------------------------------------------------
//bind
//-----------------------------------------------------------------------
//2 step: bind
//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
//int inet_pton(int af, const char *src, void *dst);
general_flag = inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
if(general_flag < 0)
{
fprintf(stdout, "analysis failed %s\n", strerror(errno));
//FIXME again!
}
if(bind(listening_socket, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
{
fprintf(stdout, "bind to %s failed, port = %d %s\n", argv[1], port, strerror(errno));
exit(1);
}
fprintf(stdout, "Bind to %s success, port = %d\n", argv[1], port);
//-----------------------------------------------------------------------
//listenning
//-----------------------------------------------------------------------
//3 step: listenning
//int listen(int sockfd, int backlog);
if((general_flag = listen(listening_socket, backlog)) < 0)
{
fprintf(stdout, "listenning failed %s\n", strerror(errno));
exit(1);
}
fprintf(stdout, "listenning success\n");
//------------------------------------------------------------------
// Set signal handler
//------------------------------------------------------------------
//typedef void (*sighandler_t) (int);
//sighandler_t signal(int signum, sighandler_t handler);
signal(SIGCHLD, sigchild_handler);
//-----------------------------------------------------------------------
//accept, create new socket
//-----------------------------------------------------------------------
//4 step: accept, create new socket
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//int socket(int domain, int type, int protocol);
struct sockaddr_in clientaddr;
socklen_t clientaddr_len;
printf("Acceting connections ...\n");
while(1)
{
clientaddr_len = sizeof(clientaddr);
int fd = accept(listening_socket, (struct sockaddr*)&clientaddr, &clientaddr_len); //这个地方在接收的时候返回的是一个类似文件描述符的整型数据,而python中对于---tcp来说则是套接字对象和地址信息的元组,对于—udp来说则是数据和地址的元组.
if(fd < 0)
{
fprintf(stdout, "accept client ip = %s, port = %s Failed :%s\n", argv[1], argv[2], strerror(errno));
//FIXME how to do?
break;
}
else
{
// TODO: Add black list or white list to filter incoming connections
pid_t pid;
pid = fork();
if(pid < 0)
{
perror("failed to create process\n");
exit(1);
}
else if(0 == pid)
{
// child process
//--------------------------------------------------------------------------
//Close parent's listening_socket
//--------------------------------------------------------------------------
close(listening_socket);
//--------------------------------------------------------------------------
// r/w
//--------------------------------------------------------------------------
//5 step: read the client sended information
//ssize_t read(int fd, void *buf, size_t count);
general_n = read(fd, buffer, BUFFER_SIZE);
if(general_n < 0)
{
fprintf(stdout, "read from client failed %s\n", strerror(errno));
//FIXME how to do?
break;
}
//TODO read the request, then need to return request?
//to handler request
fprintf(stdout, "-------receive data is %s\n", buffer);
strtok_command(fd, buffer);
//--------------------------------------------------------------------------
//close new_connected_socket
//--------------------------------------------------------------------------
close(fd);
//------------------------------------------------------------------------
//Terminate
//--------------------------------------------------------------------------
exit(0);
}
else
{
close(fd);
}
}
}
close(listening_socket);
return 0;
}
void sigchild_handler(int sig) //信号处理,就简单打印了点信息
{
fprintf(stdout, "[%d] Caught signal %d.\n", getpid(), sig);
}
二》因特网协议(ftp—文件传输协议(基于tcp),NNTP—网络新闻传输协议,POP3协议—邮件接收协议,SMTP—邮件发送协议,还有很多了,不列举了…)
这是利用SMTP和POP3协议编写的收发邮件的例子(myMail.py):(电脑不能上网,不能测试了)
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#---这些因特网协议都是基于低级别的协议TCP/IP来创建新的,更具体的,符合我们需要的服务;至于更加深入的内容,需要花力气了
from smtplib import SMTP
from poplib import POP3
from time import sleep
if __name__ == "__main__":
SMTPSVR = 'smtp.python.is.cool' #SMTP用于发送服务器,修改为自己的邮箱服务器;比如smtp.163.com
POP3SVR = 'pop.python.is.cool' #POP3用于接收邮件的服务器
#以下是构建消息头,消息体和消息内容,组合成一个完整的消息
#依据Sendmail(from, to, msg[,mopts, ropts])函数原型来构造
origHdrs = ['From:wesley@python.is.cool', \
'To:wesley@python.is.cool', \
'Subject: test msg']
origBody = ['xxx', 'yyy', 'zzz']
#按要求需要在消息前加入两个回车和换行;在头和体时加入一个回车和换行,这是RFC文档要求
origMsg = '\r\n\r\n'.join(['\r\n'.join(origHdrs), \
'\r\n'.join(origBody)])
try: #后面很多地方都需要出错处理的...
sendSvr = SMTP(SMTPSVR)
except (socket.error, socket.gaierror), e:
print 'ERROR: cannot reach "%s" \n "%s"' % (SMTPSVR, e)
return
print '***Connected to host "%s"' % SMTPSVR
errs = sendSvr.sendmail('wesley@python.is.cool', \
('wesley@python.is.cool',), origMsg)
sendSvr.quit()
assert len(errs) == 0, errs #断言是否有错误,有的话,那么值就不是0,出错,并打印错误信息;否则跳过
sleep(10) #延迟等待发送
#POP3接受消息比SMTP简单多了;可以利用内建函数来设置用户和密码
recvSvr = POP3(POP3SVR)
recvSvr.user('wesley')
recvSvr.pass_('yourNeverGuess')
rsp, msg, size = recvSvr.retr(recvSvr.stat()[0]) #返回一个三元组,返回信息,消息,和字节数;stat用于获取有效的消息列表,然后选取第一条消息
sep = msg.index('') #我们空行来分割头和消息,去掉头部分
recvBody = msg[sep+1:] #不是很明白,模糊?
assert origBody == recvBody #断言是否相等,不等则显示断言失败错误