用Java编写你自己的简单HTTP服务器

HTTP是个大协议,完整功能的HTTP服务器必须响应资源请求,将URL转换为本地系统的资源名。响应各种形式的HTTP请求(GET、POST等)。处理不存在的文件请求,返回各种形式的状态码,解析MIME类型等。但许多特定功能的HTTP服务器并不需要所有这些功能。例如,很多网站只是想显示“建设中“的消息。很显然,Apache对于这样的网站是大材小用了。这样的网站完全可以使用只做一件事情的定制服务器。Java网络类库使得编写这样的单任务服务器轻而易举。

定制服务器不只是用于小网站。大流量的网站如Yahoo,也使用定制服务器,因为与一般用途的服务器相比,只做一件事情的服务器通常要快得多。针对某项任务来优化特殊用途的服务器很容易;其结果往往比需要响应很多种请求的一般用途服务器高效得多。例如,对于重复用于多页面或大流量页面中的图标和图片,用一个单独的服务器处理会更好(并且还可以避免在请求时携带不必要的Cookie,因而可以减少请求/响应数据,从而减少下载带宽,提升速度);这个服务器在启动时把所有图片文件读入内存,从RAM中直接提供这些文件,而不是每次请求都从磁盘上读取。此外,如果你不想在包含这些图片的页面请求之外单独记录这些图片,这个单独服务器则会避免在日志记录上浪费时间。

本篇为大家简要演示三种HTTP服务器:

(1) 简单的单文件服务器

(2) 重定向服务器

(3) 完整功能的HTTP服务器

简单的单文件服务器

该服务器的功能:无论接受到何种请求,都始终发送同一个文件。这个服务器命名为SingleFileHTTPServer,文件名、本地端口和内容编码方式从命令行读取。如果缺省端口,则假定端口号为80。如果缺省编码方式,则假定为ASCII。
import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class SingleFileHTTPServer extends Thread { private byte[] content; private byte[] header; private int port=80; private SingleFileHTTPServer(String data, String encoding, String MIMEType, int port) throws UnsupportedEncodingException { this(data.getBytes(encoding), encoding, MIMEType, port); } public SingleFileHTTPServer(byte[] data, String encoding, String MIMEType, int port)throws UnsupportedEncodingException { this.content=data; this.port=port; String header=”HTTP/1.0 200 OK\r\n”+ “Server: OneFile 1.0\r\n”+ “Content-length: “+this.content.length+”\r\n”+ “Content-type: “+MIMEType+”\r\n\r\n”; this.header=header.getBytes(“ASCII”); } public void run() { try { ServerSocket server=new ServerSocket(this.port); System.out.println(“Accepting connections on port “+server.getLocalPort()); System.out.println(“Data to be sent:”); System.out.write(this.content); while (true) { Socket connection=null; try { connection=server.accept(); OutputStream out=new BufferedOutputStream(connection.getOutputStream()); InputStream in=new BufferedInputStream(connection.getInputStream()); StringBuffer request=new StringBuffer(); while (true) { int c=in.read(); if (c==’\r’||c==’\n’||c==-1) { break; } request.append((char)c); } //如果检测到是HTTP/1.0及以后的协议,按照规范,需要发送一个MIME首部 if (request.toString().indexOf(“HTTP/”)!=-1) { out.write(this.header); } out.write(this.content); out.flush(); } catch (IOException e) { // TODO: handle exception }finally{ if (connection!=null) { connection.close(); } } } } catch (IOException e) { System.err.println(“Could not start server. Port Occupied”); } } public static void main(String[] args) { try { String contentType=”text/plain”; if (args[0].endsWith(“.html”)||args[0].endsWith(“.htm”)) { contentType=”text/html”; } InputStream in=new FileInputStream(args[0]); ByteArrayOutputStream out=new ByteArrayOutputStream(); int b; while ((b=in.read())!=-1) { out.write(b); } byte[] data=out.toByteArray(); //设置监听端口 int port; try { port=Integer.parseInt(args[1]); if (port<1||port>65535) { port=80; } } catch (Exception e) { port=80; } String encoding=”ASCII”; if (args.length>2) { encoding=args[2]; } Thread t=new SingleFileHTTPServer(data, encoding, contentType, port); t.start(); } catch (ArrayIndexOutOfBoundsException e) { System.out.println(“Usage:java SingleFileHTTPServer filename port encoding”); }catch (Exception e) { System.err.println(e);// TODO: handle exception } } }
SingleFileHTTPServer类本身是Thread的子类。它的run()方法处理入站连接。此服务器可能只是提供小文件,而且只支持低吞吐量的web网站。由于服务器对每个连接所需完成的所有工作就是检查客户端是否支持HTTP/1.0,并为连接生成一两个较小的字节数组,因此这可能已经足够了。另一方面,如果你发现客户端被拒*,则可以使用多线程。许多事情取决于所提供文件的大小,每分钟所期望连接的峰值数和主机上Java的线程模型。对弈这个程序复杂写的服务器,使用多线程将会有明显的收益。

Run()方法在指定端口创建一个ServerSocket。然后它进入无限循环,不断地接受连接并处理连接。当接受一个socket时,就会由一个InputStream从客户端读取请求。它查看*行是否包含字符串HTTP。如果包含此字符串,服务器就假定客户端理解HTTP/1.0或以后的版本,因此为该文件发送一个MIME首部;然后发送数据。如果客户端请求不包含字符串HTTP,服务器就忽略首部,直接发送数据。*后服务器关闭连接,尝试接受下一个连接。

而main()方法只是从命令行读取参数。从*个命令行参数读取要提供的文件名。如果没有指定文件或者文件无法打开,就显示一条错误信息,程序退出。如果文件能够读取,其内容就读入byte数组data.关于文件的内容类型,将进行合理的猜测,结果存储在contentType变量中。接下来,从第二个命令行参数读取端口号。如果没有指定端口或第二个参数不是0到65535之间的整数,就使用端口80。从第三个命令行参数读取编码方式(前提是提供了)。否则,编码方式就假定为ASCII。然后使用这些值构造一个SingleFileHTTPServer对象,开始运行。这是唯一可能的接口。

下面是测试的结果:
命令行编译代码并设置参数:

%title插图%num

telnet::

首先,启用telnet服务(如果不会,自行google之),接着测试该主机的端口:

%title插图%num

结果(可以看到请求的输出内容):

%title插图%num

HTTP协议测试:

%title插图%num

文档(这是之前一篇文章–小车动画的文档):

%title插图%num

重定向服务器

实现的功能——将用户从一个Web网站重定向到另一个站点。下例从命令行读取URL和端口号,打开此端口号的服务器可能速度会很快,因此不需要多线程。尽管日次,使用多线程可能还是会带来一些好处,尤其是对于网络带宽很低、吞吐量很小的网站。在此主要是为了演示,所以,已经将该服务器做成多线程的了。这里为了简单起见,为每个连接都启用了一个线程,而不是采用线程池。或许更便于理解,但这真的有些浪费系统资源并且显得低效。

import java.io.BufferedInputStream; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; import java.net.BindException; import java.net.ServerSocket; import java.net.Socket; import java.util.Date; public class Redirector implements Runnable { private int port; private String newSite; public Redirector(String site, int port){ this.port=port; this.newSite=site; } @Override public void run() { try { ServerSocket server=new ServerSocket(port); System.out.println(“Redirecting connection on port” +server.getLocalPort()+” to “+newSite); while (true) { try { Socket socket=server.accept(); Thread thread=new RedirectThread(socket); thread.start(); } catch (IOException e) { // TODO: handle exception } } } catch (BindException e) { System.err.println(“Could not start server. Port Occupied”); }catch (IOException e) { System.err.println(e); } } class RedirectThread extends Thread { private Socket connection; RedirectThread(Socket s) { this.connection=s; } public void run() { try { Writer out=new BufferedWriter( new OutputStreamWriter(connection.getOutputStream(),”ASCII”)); Reader in=new InputStreamReader( new BufferedInputStream(connection.getInputStream())); StringBuffer request=new StringBuffer(80); while (true) { int c=in.read(); if (c==’\t’||c==’\n’||c==-1) { break; } request.append((char)c); } String get=request.toString(); int firstSpace=get.indexOf(‘ ‘); int secondSpace=get.indexOf(‘ ‘, firstSpace+1); String theFile=get.substring(firstSpace+1, secondSpace); if (get.indexOf(“HTTP”)!=-1) { out.write(“HTTP/1.0 302 FOUND\r\n”); Date now=new Date(); out.write(“Date: “+now+”\r\n”); out.write(“Server: Redirector 1.0\r\n”); out.write(“Location: “+newSite+theFile+”\r\n”); out.write(“Content-Type: text/html\r\n\r\n”); out.flush(); } //并非所有的浏览器都支持重定向, //所以我们需要生成一个适用于所有浏览器的HTML文件,来描述这一行为 out.write(“<HTML><HEAD><TITLE>Document moved</TITLE></HEAD>\r\n”); out.write(“<BODY><H1>Document moved</H1></BODY>\r\n”); out.write(“The document “+theFile +” has moved to \r\n<A HREF=\””+newSite+theFile+”\”>” +newSite+theFile +”</A>.\r\n Please update your bookmarks”); out.write(“</BODY></HTML>\r\n”); out.flush(); } catch (IOException e) { }finally{ try { if (connection!=null) { connection.close(); } } catch (IOException e2) { } } } } /** * @param args */ public static void main(String[] args) { int thePort; String theSite; try { theSite=args[0]; //如果结尾有’/’,则去除 if (theSite.endsWith(“/”)) { theSite=theSite.substring(0,theSite.length()-1); } } catch (Exception e) { System.out.println(“Usage: java Redirector http://www.newsite.com/ port”); return; } try { thePort=Integer.parseInt(args[1]); } catch (Exception e) { thePort=80; } Thread t=new Thread(new Redirector(theSite, thePort)); t.start(); } }
HTTP测试:
侦听8010端口,此处重定向到百度:

%title插图%num

%title插图%num

main()方法提供一个非常简单的界面,读取新网站的URL(为了把链接重定向到该URL)和监听本地端口。它使用这些信息构造了一个Rredirector对象。然后它使用所生成的Runnable对象(Redirector实现了Runnable)来生成一个新线程并启动。如果没有指定端口,Rredirector则会监听80端口。

Redirectro的run()方法将服务器socket绑定与此端口,显示一个简短的状态消息,然后进入无限循环,监听连接。每次接受连接,返回的Socket对象会用来构造一个RedirectThread。然后这个RedirectThread被启动。所有与客户端进一步的交互由此新线程完成。Redirector的run()方法只是等待下一个入站连接。

RedirectThread的run()方法完成了很多工作。它先把一个Writer链接到Socket的输出流,把一个Reader链接到Socket的输入流。输入流和输出流都有缓冲。然后run()方法读取客户端发送的*行。虽然客户端可能会发送整个Mime首部,但我们会忽略这些。*行包含所有所需的信息。这一行内容可能会是这样:

GET /directory/filename.html HTTP/1.0

可能*个词是POST或PUT,也可能没有HTTP版本。

返回的输出,*行显示为:

HTTP/1.0 302 FOUND

这是一个HTTP/1.0响应吗,告知客户端要被重定向。第二行是“Date:”首部,给出服务器的当前时间。这一行是可选的。第三行是服务器的名和版本;这一行也是可选的,但蜘蛛程序可用它来统计记录*流行的web服务器。下一行是“Location:”首部,对于此服务器这是必须的。它告知客户端要重定向的位置。*后是标准的“Content-type:”首部。这里发送内容类型text/html,只是客户端将会看到的HTML。*后,发送一个空行来标识首部数据的结束。

如果浏览器不支持重定向,那么那段HTML标签就会被发送。

功能完整的HTTP服务器

这里,我们来开发一个具有完整功能的HTTP服务器,成为JHTTP,它可以提供一个完整的文档树,包括图片、applet、HTML文件、文本文件等等。它与SingleFileHTTPServer非常相似,只不过它所关注的是GET请求。此服务器仍然是相当轻量级的;看过这个代码后,我们将讨论可能希望添加的其他特性。

由于这个服务器必须为可能很慢的网络连接提供文件系统的大文件,因此要改变其方式。这里不再在执行主线程中处理到达的每个请求,而是将入站连接放入池中。由一个RequestProcessor类实例从池中移走连接并进行处理。

import java.io.File; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import org.omg.CORBA.Request; public class JHTTP extends Thread { private File documentRootDirectory; private String indexFileName=”index.html”; private ServerSocket server; private int numThreads=50; public JHTTP(File documentRootDirectory,int port , String indexFileName)throws IOException { if (!documentRootDirectory.isDirectory()) { throw new IOException(documentRootDirectory+” does not exist as a directory “); } this.documentRootDirectory=documentRootDirectory; this.indexFileName=indexFileName; this.server=new ServerSocket(port); } private JHTTP(File documentRootDirectory, int port)throws IOException { this(documentRootDirectory, port, “index.html”); } public void run(){ for (int i = 0; i < numThreads; i++) { Thread t=new Thread(new RequestProcessor(documentRootDirectory, indexFileName)); t.start(); } System.out.println(“Accepting connection on port ” +server.getLocalPort()); System.out.println(“Document Root: “+documentRootDirectory); while (true) { try { Socket request=server.accept(); RequestProcessor.processRequest(request); } catch (IOException e) { // TODO: handle exception } } } /** * @param args */ public static void main(String[] args) { File docroot; try { docroot=new File(args[0]); } catch (ArrayIndexOutOfBoundsException e) { System.out.println(“Usage: java JHTTP docroot port indexfile”); return; } int port; try { port=Integer.parseInt(args[1]); if (port<0||port>65535) { port=80; } } catch (Exception e) { port=80; } try { JHTTP webserver=new JHTTP(docroot, port); webserver.start(); } catch (IOException e) { System.out.println(“Server could not start because of an “+e.getClass()); System.out.println(e); } } }
JHTTP类的main()方法根据args[0]设置文档的根目录。端口从args[1]读取,或者使用默认的80.然后构造一个新的JHTTP线程并启动。此JHTTP线程生成50个RequestProcessor线程处理请求,每个线程在可用时从RequestProcessor池获取入站连接请求。JHTTP线程反复地接受入站连接,并将其放在RequestProcessor池中。每个连接由下例所示的RequestProcessor类的run()方法处理。此方法将一直等待,直到从池中得到一个Socket。一旦得到Socket,就获取输入和输出流,并链接到阅读器和书写器。接着的处理,除了多出文档目录、路径的处理,其他的同单文件服务器。

import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; import java.net.Socket; import java.util.Date; import java.util.List; import java.util.LinkedList; import java.util.StringTokenizer; public class RequestProcessor implements Runnable { private static List pool=new LinkedList(); private File documentRootDirectory; private String indexFileName=”index.html”; public RequestProcessor(File documentRootDirectory,String indexFileName) { if (documentRootDirectory.isFile()) { throw new IllegalArgumentException(); } this.documentRootDirectory=documentRootDirectory; try { this.documentRootDirectory=documentRootDirectory.getCanonicalFile(); } catch (IOException e) { } if (indexFileName!=null) { this.indexFileName=indexFileName; } } public static void processRequest(Socket request) { synchronized (pool) { pool.add(pool.size(),request); pool.notifyAll(); } } @Override public void run() { //安全性检测 String root=documentRootDirectory.getPath(); while (true) { Socket connection; synchronized (pool) { while (pool.isEmpty()) { try { pool.wait(); } catch (InterruptedException e) { } } connection=(Socket)pool.remove(0); } try { String fileName; String contentType; OutputStream raw=new BufferedOutputStream(connection.getOutputStream()); Writer out=new OutputStreamWriter(raw); Reader in=new InputStreamReader(new BufferedInputStream(connection.getInputStream()), “ASCII”); StringBuffer request=new StringBuffer(80); while (true) { int c=in.read(); if (c==’\t’||c==’\n’||c==-1) { break; } request.append((char)c); } String get=request.toString(); //记录日志 System.out.println(get); StringTokenizer st=new StringTokenizer(get); String method=st.nextToken(); String version=””; if (method==”GET”) { fileName=st.nextToken(); if (fileName.endsWith(“/”)) { fileName+=indexFileName; } contentType=guessContentTypeFromName(fileName); if (st.hasMoreTokens()) { version=st.nextToken(); } File theFile=new File(documentRootDirectory,fileName.substring(1,fileName.length())); if (theFile.canRead()&&theFile.getCanonicalPath().startsWith(root)) { DataInputStream fis=new DataInputStream(new BufferedInputStream(new FileInputStream(theFile))); byte[] theData=new byte[(int)theFile.length()]; fis.readFully(theData); fis.close(); if (version.startsWith(“HTTP “)) { out.write(“HTTP/1.0 200 OK\r\n”); Date now=new Date(); out.write(“Date: “+now+”\r\n”); out.write(“Server: JHTTP 1.0\r\n”); out.write(“Content-length: “+theData.length+”\r\n”); out.write(“Content-Type: “+contentType+”\r\n\r\n”); out.flush(); } raw.write(theData); raw.flush(); }else { if (version.startsWith(“HTTP “)) { out.write(“HTTP/1.0 404 File Not Found\r\n”); Date now=new Date(); out.write(“Date: “+now+”\r\n”); out.write(“Server: JHTTP 1.0\r\n”); out.write(“Content-Type: text/html\r\n\r\n”); out.flush(); } out.write(“<HTML>\r\n”); out.write(“<HEAD><TITLE>File Not Found</TITLE></HRAD>\r\n”); out.write(“<BODY>\r\n”); out.write(“<H1>HTTP Error 404: File Not Found</H1>”); out.write(“</BODY></HTML>\r\n”); } }else {//方法不等于”GET” if (version.startsWith(“HTTP “)) { out.write(“HTTP/1.0 501 Not Implemented\r\n”); Date now=new Date(); out.write(“Date: “+now+”\r\n”); out.write(“Server: JHTTP 1.0\r\n”); out.write(“Content-Type: text/html\r\n\r\n”); out.flush(); } out.write(“<HTML>\r\n”); out.write(“<HEAD><TITLE>Not Implemented</TITLE></HRAD>\r\n”); out.write(“<BODY>\r\n”); out.write(“<H1>HTTP Error 501: Not Implemented</H1>”); out.write(“</BODY></HTML>\r\n”); } } catch (IOException e) { }finally{ try { connection.close(); } catch (IOException e2) { } } } } public static String guessContentTypeFromName(String name) { if (name.endsWith(“.html”)||name.endsWith(“.htm”)) { return “text/html”; }else if (name.endsWith(“.txt”)||name.endsWith(“.java”)) { return “text/plain”; }else if (name.endsWith(“.gif”)) { return “image/gif”; }else if (name.endsWith(“.class”)) { return “application/octet-stream”; }else if (name.endsWith(“.jpg”)||name.endsWith(“.jpeg”)) { return “image/jpeg”; }else { return “text/plain”; } } }
不足与改善:
这个服务器可以提供一定的功能,但仍然十分简单,还可以添加以下的一些特性:

(1) 服务器管理界面

(2) 支持CGI程序和Java Servlet API

(3) 支持其他请求方法

(4) 常见Web日志文件格式的日志文件

(5) 支持多文档根目录,这样各用户可以有自己的网站

*后,花点时间考虑一下可以采用什么方法来优化此服务器。如果真的希望使用JHTTP运行高流量的网站,还可以做一些事情来加速此服务器。*点也是*重要的一点就是使用即时编译器(JIT),如HotSpot。JIT可以将程序的性能提升大约一个数量级。第二件事就是实现智能缓存。记住接受的请求,将*频繁的请求文件的数据存储在Hashtable中,使之保存在内存中。使用低优先级的线程更新此缓存。

解决iOS8安装企业版无反应问题

iOS7可以下载没有任何问题,iOS8发现挂在官网上的企业版的app点击了提示是否安装应用程序,但是确认以后没有反应,找了很久,都没有发现问题。后来查看了的device console发现安装的时候出现

<span style="font-size: 24px;"><code><span style="color: rgb(255, 0, 0);"><span class="typ">LoadExternalDownloadManifestOperation</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Ignore</span><span class="pln"> manifest download</span><span class="pun">,</span><span class="pln"> already have bundleID</span><span class="pun">:</span><span class="pln"> com</span><span class="pun">.</span><span class="pln">mycom</span><span class="pun">.</span><span class="typ">MyApp</span></span></code></span>

后来查资料外国开发者推测是iOS8的一个bug:

The biggest issue for us is that we can not reproduce this onany of our devices. Our suspicion is that iOS 8 has some internalcache with bundle IDs and just doesn’t install a build if it thinksthat an app with this bundle ID is already installed. As theinstallation doesn’t even start, we think that iOS is matching thebundle identifier from the manifest plist against this cache.

它会寻找是否ios缓存的identifier与bundle identifier在plist文件中匹配,如果匹配,它会认为已经安装了,就不会有反应。 上面解释的很清楚。所以解决办法就是在plist文件中修改bundle Identifier。

比如你的plist文件的BundleID是com.mycom.MyApp,则修改成com.mycom.MyApp.fixios8。(创建一个假的bundleID,可以随便取,这样ios就不会认为你已经安装。记住是修改plist文件的bundleID,不是应用程序的bundleID)

发布以后就发现可以了。只是如果你已经安装了app,则会出现一个新的下载的空白icon,因为这个app的bundleID与你plist的bundleID不一致,当下载完成后,会覆盖原本app,因为它会检测到下载安装的app的bundleID已经存在并且覆盖。

完美解决。

IOS 获取UIView触摸响应事件

有两种方法

1、注册手势

用  UIGestureRecognizer  的好处在于有现成的手势,开发者不用自己计算手指移动轨迹。 UIGestureRecognizer 的衍生类別有以下几种:
•    UITapGestureRecognizer
•    UIPinchGestureRecognizer
•    UIRotationGestureRecognizer
•    UISwipeGestureRecognizer
•    UIPanGestureRecognizer
•    UILongPressGestureRecognizer

从命名上不难了解這些类別所对应代表的手势,分別是 Tap (点一下)、 Pinch (二指往內或往外拨动)、 Rotation (旋转)、 Swipe (滑动,快速移动)、 Pan (拖移,慢速移动)以及 LongPress (长按)。這些手势別在使用上也很简单,只要在使用前定义并添加到对应的视图上即可。
2、重新UIView触摸响应事件

– ( void )touchesBegan:( NSSet *)touches withEvent:( UIEvent *)event
{
[ super touchesBegan :touches withEvent :event];

}
– ( void )touchesMoved:( NSSet *)touches withEvent:( UIEvent *)event
{
[ super touchesMoved :touches withEvent :event];
}
– ( void )touchesEnded:( NSSet *)touches withEvent:( UIEvent *)event
{
[ super touchesEnded :touches withEvent :event];
}

当手指接触屏幕时,就会调用 touchesBegan:withEvent 方法;
当手指在屏幕上移时,动就会调用 touchesMoved:withEvent 方法;
当手指离开屏幕时,就会调用 touchesEnded:withEvent 方法;
//

注意:采用重写方式,不要用手势否则会获取不到touchesEnded事件。

iOS 获取整个app在屏幕上的点击坐标和点击事件

项目中有个需求是想拿到app里所有在屏幕上的点击坐标
解决方案创建一个子类继承自UIApplication,然后在sendEvent方法中获取并判断

#import “MRApplication.h”
#include <CommonCrypto/CommonCrypto.h>

@interface MRApplication()

@property(nonatomic,assign) BOOL isMoved;

@end

@implementation MRApplication

– (void)sendEvent:(UIEvent *)event{
if (event.type==UIEventTypeTouches) {
UITouch *touch = [event.allTouches anyObject];

if (touch.phase == UITouchPhaseBegan) {
self.isMoved = NO;
}

if (touch.phase == UITouchPhaseMoved) {
self.isMoved = YES;
}

if (touch.phase == UITouchPhaseEnded) {
if (!self.isMoved && event.allTouches.count == 1) {
UITouch *touch = [event.allTouches anyObject];
CGPoint locationPointWindow = [touch preciseLocationInView:touch.window];
NSLog(@”TouchLocationWindow:(%.1f,%.1f)”,locationPointWindow.x,locationPointWindow.y);
}
self.isMoved = NO;
}
}
[super sendEvent:event];
}

@end

其实在touch对象中已经有了View的信息,如果想获取在view中的相对坐标也可以.使用touch.view即可
CGPoint locationPointWindow = [touch preciseLocationInView:touch.view];

注意:这个MRApplication需要在main.m中引入,然后就可以拦截整个app所有的点击事件了,其中我对滑动和多点触控做了处理,不加if判断是会拿到滑动和多点触控时的UIEvent

  1. #import <UIKit/UIKit.h>
  2. #import “AppDelegate.h”
  3. #import “MRApplication.h”
  4. int main(int argc, char * argv[]) {
  5. @autoreleasepool {
  6. return UIApplicationMain(argc, argv, NSStringFromClass([MRApplication class]), NSStringFromClass([AppDelegate class]));
  7. }
  8. }

企业如何部署云计算? 这三点要雨露均沾

在信息经济的驱动下,为降低企业运营成本,云计算服务正迎来爆发式增长,成为国内外互联网巨头角逐的新战场。目前的IT市场,云计算的使用率越来越高,越来越多的用户接受并使用云计算。据测算,在企业和个人需求的推升下,云计算产业链将达数千亿市场规模,相关的数据中心、系统服务、基础设备等相关产业,也有望获得实质性需求支撑。

1 如何理解云计算?

一句话通俗的来说,云计算就是通过网络以自助服务的方式获得所需要的IT资源的模式。

获取路径:通过网络

获取方式:自助服务

获取对象:IT资源(包括不限于计算能力、存储能力、带宽、DNS等)

优点:按需付费,多快好省

云计算面向的用户群体包含:

个人站长

中小微企业(非互联网)

互联网企业

中大型传统企业(民营企业、央企、国企等)

各级政府机关

教育部门、科研机构

地方论坛

640.webp (1)

从技术角度来看,云计算是一系列技术的结合,例如虚拟化技术,大规模存储技术,分布式系统技术等。美国国家标准与技术研究院(NIST)把云计算分为三类:

软件即服务(SaaS),这种解决方案通过云来交付软件应用。

平台即服务(PaaS),这类解决方案提供在线访问计算资源和编程工具,开发人员可以利用这些资源和编程工具来创建和控制基于云计算的应用。

基础设施即服务(IaaS),这类解决方案通过Web提供像存储空间和处理能力这样的基础设施资源。亚马逊网络服务就是*著名的IaaS提供商之一。

无论是哪一类云计算解决方案,它们都能通过“公共云”(public cloud)或“私有云”(private cloud)来交付。公共云解决方案通过互联网交换数据。在用户端,除了一个Web浏览器和高速互联网连接外,它们不需要任何基础设施,其典型的收费方式为订阅型现收现付制,您只需为所使用的资源付费。私有云解决方案利用相同的基础技术和协议作为其公共对等物,但驻留在企业防火墙内的私有或租用的服务器上,许多IT经理人们都相信,私有云方案使他们的安全和管理工作更加轻松。

640.webp (2)

  无论是公共云还是私有云解决方案,都能够带来大量的商业和技术方面的效益:

效率:云解决方案通常运行在灵活、高度自动化的基础架构上,它们在管理上更加简单,因而成本更低。另外,由于它们不需要本地服务器资源,公共云解决方案使各企业避免了巨大的前期软硬件投资,使IT由资本支出转为费用。

灵活性:大多数云基础架构都广泛采用服务器虚拟化技术,这种技术允许单个物理服务器宿主成多个“虚拟服务器”,而每个虚拟服务器都有其自己的操作系统和应用程序。由于那些虚拟服务器本质上只是些复杂的计算机文件,创建一个虚拟服务器所需的时间只相当于建立一个新的物理服务器的一小部分。因此,云环境使企业部署新的应用和服务的速度远远高于传统环境。

2 企业云计算部署形态

企业在考虑云计算部署时,还面临一个重要问题,选择什么形式的云计算才是*适合自身需求的。调查数据显示,中国市场对私有云和混合云的接受速度快,有明确的IT投入。63%的受访者会首先考虑部署混合云或者私有云。其中,考虑部署私有云和混合云的受访用户均分别占比31.5%。中国目前尚处于云计算市场教育阶段,中国用户首选私有云,认为私有云能够带给企业更好的可控性和安全性。同时,中国市场对公有云接受度较低,目前,考虑部署比例仅为6.5%。

640.webp (4)

就企业规模对不同云计算接受度调查数据显示,相对于中小企业,企业级用户更青睐私有云,其次是混合云。调研显示,企业级用户中,考虑部署私有云的比例高达46.4%,而中小企业仅占比24.1%。而在混合云的部署上,中小企业的部署比例(34.2%)要高于企业级用户(26.1%)。对于企业级用户而言,安全性的战略价值高于经济性。在中国经济快速发展的过程中,企业数据安全性、性能稳定性和业务连续性决定着企业的核心竞争力。

3企业如何选择云平台?

随着各大云计算厂商纷纷将自身优秀的IT技术以云服务的模式的开放,越来越多的企业开始重视云计算,以便更好的拥抱云计算,提升自身IT业务的服务能力。

据微软的一份报告显示,虽然有86%的小企业已经看到了云计算的重要性,但仍然有70%的企业还没有使用云解决方案。这就使我们清楚地意识到这样一个事实:尽管小企业知道云计算的广泛价值,但仍然不知道如何选择云服务。

那么企业如何选择云平台呢?

选择云平台服务的时候,我们首选应该考虑以下因素:

1) 是否可以保障系统的稳定性、可靠性和安全性。这是IT决策者在选择云管理平台时*重要的衡量标准之一。

2)是否可以和现在的虚拟化平台兼容。采用现有的虚拟化供应商升级云计算,不失为一种选择,但是考虑到成本以及供应商锁定的问题,也可以选择一种可以兼容现有虚拟化基础的云计算管理平台。

3)是否有完整的生命周期管理。现在虚拟机泛滥的问题很普通,导致IT管理者不清楚哪些应用在哪些虚拟机上、无法及时回收资源等等问题,多数应用不能按照不同需求定义虚拟机的服务等级。

4)是否便于管理。云计算的一个重要的优势是减少IT管理成本。

近年来,随着互联网云计算领域不断的完善和创新以及用户对个性化业务需求增长,用户对于云计算的需求也更看重云计算的技术应用。安徽八度网络有限公司基于强大的云计算技术,高度重视技术和服务的建设,加强产品创新,为国内外中小企业提供优质的产品和技术服务以顺应新时代企业发展需求。

640.webp (3)

专注企业级云服务,八度平台云系统实现了基于云计算弹性选择、按需使用、按需付费等特点和虚拟主机便捷的后台管理界面,满足了用户即时开通虚拟主机的需求。用户可以在一个管理平台上实现对于默认首页、域名绑定、数据库开通等的一键式操作,操作方便快捷,同时也减少了登录服务器的频率,提高了安全性。

为了保证用户得到更好的服务,八度网络自主研发云监控平台。用户可实时关注站点的可用性、响应时间、服务器资源等指标,并提供智能多样的报警方式、信息统计,任何系统异常都能及时通知到用户(短信通知、电话语音通知),及时处理网站异常,减少了因客户发现问题并提交后才能被解决的被动状态,为服务器安全保驾护航。

云计算的出现,在某种程度上解放了大量以前只关注底层的机房、硬件和网络运维的技术人员。一家企业*核心其实是他的业务系统,而不是机房里的那一堆机器,这些设备*大的价值是其计算能力、存储能力、网络能力能够支撑企业核心业务的开展。作为国内*的云计算服务平台。八度网络为企业用户提供基于云计算的SaaS服务,拥有云计算30组架构组,为企业提供强大的云虚拟主机控制面板功能及云虚拟主机监控系统,致力于为万千企业提供快速、稳定、安全的云虚拟主机服务。

iOS的pointInside和hitTest方法

pointInside和hitTest区别:

hitTest和pointInside是UIView提供的触摸事件处理方法。

-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event: 用来判断触摸点是否在控件上

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event: 用来判断控件是否接受事件以及找到*合适的view
事件处理流程:

(1)当用户点击屏幕时,会产生一个触摸事件,系统会将该事件加入到一个由UIApplication管理的事件队列中

(2)UIApplication会从事件队列中取出*前面的事件进行分发以便处理,通常,先发送事件给应用程序的主窗口(UIWindow)

(3)主窗口会调用hitTest:withEvent:方法在视图(UIView)层次结构中找到一个*合适的UIView来处理触摸事件
(hitTest:withEvent:其实是UIView的一个方法,UIWindow继承自UIView,因此主窗口UIWindow也是属于视图的一种)

hitTest:withEvent:方法处理流程:

(1)首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内:

若pointInside:withEvent:方法返回NO,说明触摸点不在当前视图内,则当前视图的hitTest:withEvent:返回nil
若pointInside:withEvent:方法返回YES,说明触摸点在当前视图内,则遍历当前视图的所有子视图(subviews),调用子视图的hitTest:withEvent:方法重复前面的步骤,子视图的遍历顺序是从top到bottom,即从subviews数组的末尾向前遍历,直到有子视图的hitTest:withEvent:方法返回非空对象或者全部子视图遍历完毕:
(2)

若*次有子视图的hitTest:withEvent:方法返回非空对象,则当前视图的hitTest:withEvent:方法就返回此对象,处理结束
若所有子视图的hitTest:withEvent:方法都返回nil,则当前视图的hitTest:withEvent:方法返回当前视图自身(self)
(4)*终,这个触摸事件交给主窗口的hitTest:withEvent:方法返回的视图对象去处理

使用示例:

(1)扩大Button的点击区域(上下左右各增加20)

重写自定义Button的pointInside方法:

– (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if (CGRectContainsPoint(CGRectInset(self.bounds, -20, -20), point)) {
return YES;
}
return NO;
}
(2)子view超出了父view的bounds响应事件

正常情况下,子View超出父View的bounds的那一部分是不会响应事件的,要解决这个问题,需要重写父View的pointInside方法:

– (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL flag = NO;
for (UIView *view in self.subviews) {
if (CGRectContainsPoint(view.frame, point)){
flag = YES;
break;
}
}
return flag;
}
(3)如果Button被View盖住了,在点击View时,希望该Button能够响应事件

方案1:点击View及View的非交互子View(例如UIImageView),则该Button可以响应事件

– (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL next = YES;
for (UIView *view in self.subviews) {
if ([view isKindOfClass:[UIControl class]]) {
if (CGRectContainsPoint(view.frame, point)){
next = NO;
break;
}
}
}
return !next;
}
方案2:

– (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
CGPoint btnP = [self convertPoint:point toView:self.btn];
if ([self.btn pointInside:btnP withEvent:event]) {
//如果触点在后面按钮上,可以返回按钮,让按钮响应事件
return self.btn;
}else{
//如果不在就按照系统默认做法
return [super hitTest:point withEvent:event];
}
}

iOS多线程NSThread的使用

1、创建线程
detach方法:直接创建并启动一个线程,由于没有返回值,如果需要获取新创建的线程,需要在执行的selector方法中调用-[NSThread currentThread]获取。

+ (void)detachNewThreadWithBlock:(void (^)(void))block;
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
init方法:初始化线程,并返回NSThread对象,线程创建出来之后需要手动调用-start方法启动。

– (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;
– (instancetype)initWithBlock:(void (^)(void))block;
2、线程操作
创建好线程之后需要对线程进行操作,NSThread给线程提供的主要操作方法有启动、睡眠、取消、退出

1、启动
我们使用init方法将线程创建出来之后, 线程并不会立即运行, 只有我们手动调用-start方法之后才会启动线程(detachNewThread创建的线程会自动启动)。

– (void)start;
注意: 部分线程属性需要在启动前设置 , 线程启动之后再设置会无效. 如qualityOfService属性。

2、睡眠
NSThread提供了2种让线程睡眠的方法,一个是根据NSDate传入睡眠时间,一个是直接传入NSTimeInterval(睡眠时间)。

+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
这两个方法作用一样,相当于执行一个sleep的任务。在执行过程中,即使有其他任务传入runloop,runloop也不会立即响应,必须sleep任务完成之后,才会响应其他任务。

调用睡眠方法后,线程会立即让出当前时间片,让出CPU资源,进入阻塞状态。

3、取消
对于线程的取消,NSThread提供了一个取消的方法和一个属性。

– (void)cancel;

@property (readonly, getter=isCancelled) BOOL cancelled;
调用-cancel方法并不会立刻取消线程,它仅仅是将cancelled属性设置为YES。cancelled也仅仅是一个用于记录状态的属性。线程取消的功能需要我们在main函数中自己实现。

要实现取消的功能,我们需要自己在线程的main函数中定期检查isCancelled状态来判断线程是否需要退出,当isCancelled为YES的时候,我们手动退出。如果我们没有在main函数中检查isCancelled状态,那么调用cancel将没有任何意义。

– (void)onThread {
for (int i = 0; i < 5; i++) {
//睡眠1秒
[NSThread sleepForTimeInterval:1];
//判断是否已经取消
if (NSThread.currentThread.isCancelled) {
return;
}
}
}
4、退出
与充满不确定性的-cancel相比,+exit函数可以让线程立即退出。

+ (void)exit;
+exit 调用之后会立即终止线程,即使任务还没有执行完成也会中断,这就非常有可能导致内存泄漏等严重问题,所以一般不推荐使用。

– (void)onThread {
for (int i = 0; i < 5; i++) {
//睡眠1秒
[NSThread sleepForTimeInterval:1];
if (i == 3) {
[NSThread exit];
}
}
}
对于有runloop的线程,可以使用CFRunLoopStop()结束runloop配合-cancel结束线程

– (void)onThread {
// 给线程设置名字
[[NSThread currentThread] setName:@”myThread”];
// 给线程添加runloop
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
// 给runloop添加数据源
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
// 检查isCancelled
While(![[NSThread currentThread] isCancelled]) {
// 启动runloop
[runloop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10];
}
}
5、线程状态
//判断线程是否正在执行
@property (readonly, getter=isExecuting) BOOL executing;

//判断线程是否结束
@property (readonly, getter=isFinished) BOOL finished;

//判断线程是否被取消
@property (readonly, getter=isCancelled) BOOL cancelled;
3、线程通讯
线程准备好之后,经常需要从主线程把耗时的任务丢给辅助线程,当任务完成之后辅助线程再把结果传回主线程,这些线程通讯一般用的都是perform方法:

// 1、将selector丢给主线程执行, 可以指定runloop mode
– (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;

// 2、将selector丢给主线程执行, runloop mode默认为common mode
– (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;

// 3、创建一个后台线程执行selector
– (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg;

// 4、将selector丢给指定线程执行, 可以指定runloop mode
– (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array API_AV;

// 5、将selector丢给指定线程执行, runloop mode默认为default mode
– (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
所以我们一般用3、4、5方法将任务丢给辅助线程,任务执行完成之后再使用1、2方法将结果传回主线程;
注意: perform方法只对拥有runloop的线程有效,如果创建的线程没有添加runloop,perform的selector将无法执行;

4、线程优先级
NSThread有4个优先级的API:

+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;

@property double threadPriority;
@property NSQualityOfService qualityOfService
前两个是类方法, 用于设置和获取当前线程的优先级。
threadPriority属性可以通过对象来设置和获取优先级。
由于线程优先级是一个比较抽象的东西,没人知道0.5和0.6到底有多大区别,所以iOS8之后新增了qualityOfService枚举属性,大家可以通过枚举值设置优先级。

typedef NS_ENUM(NSInteger, NSQualityOfService) {
// *高优先级, 主要用于直接参与提供交互式UI的工作, 例如: 处理点击事件或绘制图像到屏幕上
NSQualityOfServiceUserInteractive = 0x21,
// 次高优先级, 用于执行用户明确请求的工作, 并且必须立即显示结果, 以便进行进一步的用户交互
NSQualityOfServiceUserInitiated = 0x19,
// 默认优先级, 当没有设置优先级的时候, 线程默认优先级
NSQualityOfServiceUtility = 0x11,
// 普通优先级,主要用于不需要立即返回的任务
NSQualityOfServiceBackground = 0x09,
// 后台优先级, 用于完全不紧急的任务
NSQualityOfServiceDefault = -1
};
一般主线程和没有设置优先级的线程都是默认优先级。

5、主线程和当前线程
NSThread提供了非常方便的获取和判断主线程的API:

// 判断当前线程是否是主线程
@property (readonly) BOOL isMainThread;
@property (class, readonly) BOOL isMainThread;

// 获取主线程的thread
@property (class, readonly, strong) NSThread *mainThread;

// 获取当前线程
@property (class, readonly, strong) NSThread *currentThread;
6、线程通知
NSThread有三个线程相关的通知:

// 由当前线程派生出*个其他线程时发送, 一般一个线程只发送一次
FOUNDATION_EXPORT NSNotificationName const NSWillBecomeMultiThreadedNotification;

// 这个通知目前没有实际意义, 可以忽略
FOUNDATION_EXPORT NSNotificationName const NSDidBecomeSingleThreadedNotification;

// 线程退出之前发送这个通知
FOUNDATION_EXPORT NSNotificationName const NSThreadWillExitNotification;
7、解决线程冲突
同步代码块
使用@synchronized包围的代码即为同步代码块,它可以防止不同的线程同时执行同一段代码。

– (void)onThread {
@synchronized (self) {
self.money += 10;
[NSThread sleepForTimeInterval:0.1];
}
}
锁机制
创建一个NSLock类的锁对象,lock方法用于加锁,如果锁被其他对象占用则线程被阻塞,unlock方法用于解锁,以便其他线程加锁。

self.lock = [[NSLock alloc] init];

– (void)onThread {
[self.lock lock];
[NSThread sleepForTimeInterval:0.1];
self.money -= 10;
[self.lock unlock];
}
线程间通信
线程的调度对于开发者来说是透明的,我们不能也无法预测线程执行的顺序,但有时我们需要线程按照一定条件来执行,这时就需要线程间进行通信,NSCondition提供了线程间通信的方法。

@interface NSCondition : NSObject <NSLocking> {
@private
void *_priv;
}

/*
调用NSCondition对象wait方法的线程会阻塞,直到其他线程调用该对象的signal方法或broadcast方法来唤醒
唤醒后该线程从阻塞态改为就绪态,交由系统进行线程调度
执行wait方法时内部会自动执行unlock方法释放锁,并阻塞线程
*/
– (void)wait;

//同上,只是该方法是在limit到达时唤醒线程
– (BOOL)waitUntilDate:(NSDate *)limit;

/*
唤醒在当前NSCondition对象上阻塞的一个线程
如果在该对象上wait的有多个线程则随机挑选一个,被挑选的线程则从阻塞态进入就绪态
*/
– (void)signal;

/*
同上,该方法会唤醒在当前NSCondition对象上阻塞的所有线程
*/
– (void)broadcast;

@property (nullable, copy) NSString *name;

@end
NSCondition实现了NSLocking协议,所以NSCondition同样具有锁的功能,与NSLock一样可以加锁与解锁的操作。

self.condition = [[NSCondition alloc] init];

NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(onThread1) object:nil];
[thread1 start];

NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(onThread2) object:nil];
[thread2 start];

– (void)onThread1 {
while (true)
{
//首先使用condition上锁,如果其他线程已经上锁则阻塞
[self.condition lock];
if (self.haveMoney)
{
self.haveMoney = NO;
//唤醒其他在condition上等待的线程
[self.condition broadcast];
}
else
{
//如果没钱则等待,并阻塞
[self.condition wait];
//如果阻塞的线程被唤醒后会继续执行代码
}
//释放锁
[self.condition unlock];
}
}

– (void)onThread2 {
while (true)
{
//首先使用condition上锁,如果其他线程已经上锁则阻塞
[self.condition lock];
if (!self.haveMoney)
{
self.haveMoney = YES;
//唤醒其他在condition上等待的线程
[self.condition broadcast];
}
else
{
//如果有钱则等待,并阻塞
[self.condition wait];
//如果阻塞的线程被唤醒后会继续执行代码
}
//释放锁
[self.condition unlock];
}
}

java开发之服务器端开发

第二种: 服务器端开发
*个阶段是连接器设计,主要涉及到涉及到网络知识(阻塞式socket和非阻塞式socket)、通信模式(TCP/IP或者UDP)、网络管理(负载、连接状态等)、传输协议设计(特别与其他语言的交互)、I/O处理(bytes转成Object、Object转成bytes)等。有些方面可以靠网络硬件设备来解决。
第二个阶段是核心业务设计(严格来讲就是通用服务设计)
多线程的设计(参考《多线程设计模式》)、管理设计(参考JMX)、消息传输设计(参考JMS)、安全设计(参考java安全)、缓存设计(参考cache4j、JCS等开源项目)、资源池设计(数据库连接池等)、对象重用设计、命名空间设计(参考JNDI)、分布式设计(参考RMI)、组件设计
第三个阶段是前面两个阶段基础上的相关应用开发了,主要涉及的知识为数据库方面的知识,主要包括事务处理、数据库建表设计、数据持久化设计等。
*后是服务器端的部署,包括JVM的参数调优,批处理文件编写,目录结构等

什么是云计算?为什么需要云?

【核心提示: 云计算 不仅带来了IT模式的变化,也将引发IT服务的变革,企业和用户只需要关注数据是自己的,而对数据的计算存储方式、效率均采用云的服务来实现和提升,云的供应商则将核心业务重点放在IT架构的运营上,服务将成为下一代IT的核心内容。】

2007年以来,云计算成为IT领域*令人关注的话题之一,也是当前大型企业、互联网的IT建设正在考虑和投入的重要领域。云计算的提出,引发了新的技术变革和新的IT服务模式。但是对大多数而言人,云还是一种还不确切的定义,什么是云,为什么需要云?

1 为什么需要云计算?

企业的IT建设过程,以当前的基准来衡量,主要有三个阶段,如图1所示。

%title插图%num
图1 企业IT向云计算演进路线图

  *个阶段:大集中过程。

这一过程将企业分散的数据资源、IT资源进行了物理集中,形成了规模化的数据中心基础设施。在数据集中过程中,不断实施数据和业务的整合,大多数企业的数据中心基本完成了自身的标准化,使得既有业务的扩展和新业务的部署能够规划、可控,并以企业标准进行IT业务的实施,解决了数据业务分散时期的混乱无序问题。

在这一阶段中,很多企业在数据集中后期也开始了容灾建设,特别是在雪灾、大地震之后,企业的容灾中心建设普遍受到重视,以金融为热点行业几乎开展了全行业的容灾建设热潮,并且金融行业的大部分容灾建设的级别都非常高,面向应用级容灾(数据零丢失为目标)。

总的来说,*阶段过程解决了企业IT分散管理和容灾的问题。

第二个阶段:实施虚拟化的过程。

在数据集中与容灾实现之后,随着企业的快速发展,数据中心IT基础设施扩张很快,但是系统建设成本高、周期长,即使是标准化的业务模块建设(哪怕是系统的复制性建设),软硬件采购成本、调试运行成本与业务实现周期并没有显著下降。标准化并没有给系统带来灵活性,集中的大规模IT基础设施出现了大量系统利用率不足的问题,不同的系统运行在独占的硬件资源中,效率低下而数据中心的能耗、空间问题逐步突显出来。因此,以降低成本、提升IT运行灵活性、提升资源利用率为目的的虚拟化开始在数据中心进行部署。

虚拟化屏蔽了不同物理设备的异构性,将基于标准化接口的物理资源虚拟化成逻辑上也完全标准化和一致化的逻辑计算资源(虚拟机)和逻辑存储空间。虚拟化可以将多台物理服务器整合成单台,每台服务器上运行多种应用的虚拟机,实现物理服务器资源利用率的提升,由于虚拟化环境可以实现计算与存储资源的逻辑化变更,特别是虚拟机的克隆,使得数据中心IT实施的灵活性大幅提升,业务部署周期可用数月缩小到一天以内。虚拟化后,应用以VM为单元部署运行,数据中心服务器数量可大为减少且计算能效提升,使得数据中心的能耗与空间问题得到控制。

总的来说,第二阶段过程提升了企业IT架构的灵活性,数据中心资源利用率有效提高,运行成本降低。

第三个阶段:云计算阶段。

对企业而言,数据中心的各种系统(包括软硬件与基础设施)是一大笔资源投入。新系统(特别是硬件)在建成后一般经历3-5年即面临逐步老化与更换,而软件技术则不断面临升级的压力。另一方面,IT的投入难以匹配业务的需求,即使虚拟化后,也难以解决不断增加的业务对资源的变化需求,在一定时期内扩展性总是有所限制。

于是企业IT产生新的期望蓝图:IT资源能够弹性扩展、按需服务,将服务作为IT的核心,提升业务敏捷性,进一步大幅降低成本。因此,面向服务的IT需求开始演化到云计算架构上。云计算架构可以由企业自己构建,也可采用第三方云设施,但基本趋势是企业将逐步采取租用IT资源的方式来实现业务需要,如同水力、电力资源一样,计算、存储、网络将成为企业IT运行的一种被使用的资源,无需自己建设,可按需获得。

从企业角度,云计算解决了IT资源的动态需求和*终成本问题,使得IT部门可以专注于服务的提供和业务运营。

这三个阶段中,大集中与容灾是面向数据中心物理组件和业务模块,虚拟化是面向数据中心的计算与存储资源,云计算*终面向IT服务。这样一个演进过程,表现出IT运营模式的逐步改变,而云计算则*终根本改变了传统IT的服务结构,它剥离了IT系统中与企业核心业务无关的因素(如IT基础设施),将IT与核心业务完全融合,使企业IT服务能力与自身业务的变化相适应。

在技术变革不断发生的过程中,网络逐步从基本互联网功能转换到WEB服务时代(典型的WEB2.0时代),IT也由企业网络互通性转换到提供信息架构全面支撑企业核心业务。技术驱动力也为云计算提供了实现的客观条件,如图2所示,在关键领域云计算技术已经就绪:

%title插图%num
图2 云计算的技术基础

标准化:公共技术的长期发展,使得基础组件的标准化非常完善,硬件层面的互通已经没有阻碍(即使是非常封闭的大型机目前也开始支持对外直接出IP接口),大规模运营的云计算能够*大降低单位建设成本。

  虚拟化与自动化:虚拟化技术不断纵深发展,IT资源已经可以通过自动化的架构提供全局动态调度能力,自动化提升了IT架构的伸缩性和扩展性。

并行/分布式架构:大规模的计算与数据处理系统已经在分布式、并行处理的架构上得到广泛应用,计算密集、数据密集、大型数据文件系统成为云计算的实现基础,从而要求整个基础架构具有更高的弹性与扩展性。

带宽:大规模的数据交换需要超高带宽的支撑,网络平台在40G/100G能力下可具备更扁平化的结构,使得云计算的信息交互以*短快速路径执行。

因此,从传统WEB服务向云计算服务发展已经具备技术基础,而企业的IT从信息架构演进到弹性的IT服务也成为必然。

2 云计算的定义与基本模型

云计算的定义

目前,云计算没有统一的定义,这也与云计算本身特征很相似。维基百科对云计算的定义是:云计算是一种基于互联网的计算新方式,通过互联网上异构、自治的服务为个人和企业提供按需即取的计算。由于资源是在互联网上,而互联网通常以云状图案来表示,因此以云来类比这种计算服务,同时云也是对底层基础设施的一种抽象概念。云计算的资源是动态扩展且虚拟化的,通过互联网提供,终端用户不需要了解云中基础设施的细节,不必具有专业的云技术知识,也无需直接进行控制,只关注自身真正需要什么样的资源以及如何通过网络来获得相应的服务。

关于云计算的描述,在当前具有的共同特征是:云是一种服务,类似水电一样,按需使用、灵活付费,使用者只关注服务本身。H3C的云计算理念认为云计算是一种新的IT服务模式,支持大规模计算资源的虚拟化,提供按需计算、动态部署、灵活扩展能力。

图3给出了一个用户使用云计算的示意,用户对云资源的使用不用关注具体技术实现细节,只需关注业务的体验。比如当前被广泛使用的搜狗拼音输入法,它其实就是一种云服务:搜狗输入法能够以快速简单的方式为使用者提供需要的语境、备选的语素,使得文字的编排可以成为激发灵感的一个辅助工具;但是用户并不关注搜狗输入法在后台运行的数千台服务器提供的大型集群计算,这些工作都交给了ISP。

%title插图%num
图3 云计算视图

  云计算模型

对于云计算的分类,目前比较统一的方式是从以下两个维度进行划分。

Ÿ 按服务的层次。

%title插图%num
图4 云计算的服务层次

 如图4所示,*底层是IT Foundation,严格来说这并不是云计算的一层,是构成云计算的IT基础设施的集合,具体表现为不同的软硬件厂商的产品与方案。

  云计算服务的基础层次是IaaS(Infrastructure as a Service,基础架构即服务)。在这一层面,通过虚拟化、动态化将IT基础资源(计算、网络、存储)形成资源池。资源池即是计算能力的集合,终端用户(企业)可以通过网络获得自己所需要的计算资源,运行自己的业务系统,这种方式使用户不必自己建设这些基础设施,而只是通过对所使用资源付费即可。

在IaaS之上是PaaS(Platform as a Service,平台即服务)层。这一层面除了提供基础计算能力,还具备了业务的开发运行环境,对于企业或终端用户而言,这一层面的服务可以为业务创新提供快速低成本的环境。

*上层是SaaS(Soft as a Service,软件即服务)。SaaS可以说在云计算概念出现之前已经有了,而随着云计算技术的发展而得到了更好的支撑。SaaS的软件是拿来即用的,不需要用户安装,因为SaaS真正运行在ISP的云计算中心,SaaS的软件升级与维护也无需终端用户参与,SaaS是按需使用的软件,传统软件买了一般是无法退货的,而SaaS是灵活收费的,不使用就不付费。

层次化的云计算一般也称为I-P-S云计算,各层可独立提供云服务,下一层的架构也可以为上一层云计算提供支撑。仍以搜狗拼音为例,由大型服务器群、高速网络、存储系统等组成的IaaS架构为内部的业务开发部门提供基础服务,而内部业务开发系统在IaaS上构建了PaaS,并部署运行搜狗拼音应用系统,这样一个大型的系统对互联网用户而言,就是一个大规模SaaS应用。

Ÿ 按云的归属。

%title插图%num
图5 云的归属

  如图5所示,主要分为公有云、私有云和混合云。公有云一般属ISP构建,面向公众、企业提供公共服务,由ISP运营;私有云是指由企业自身构建的为内部提供云服务;当企业既有私有云,同时又采用公共云计算服务,这两种云之间形成一种内外数据相互流动的形态,便是混合云的模式。

3 云计算的基础架构要求

从本质上来说,云计算是一种IT模式的改变,这种变化使得IT基础架构的运营专业化程度不断集中和提高从而对基础架构层面提出更高的要求。如图6所示,云计算聚焦于高性能、虚拟化、动态性、扩展性、灵活性、高安全,简化用户的IT管理,提升IT运行效率,大幅节省成本。

%title插图%num
图6 云计算对基础架构的关注点

云计算的基础架构主要以计算(服务器)、网络、存储构成,为满足云计算的上述要求,各基础架构层面都有自身的要求。如图7所示,对于服务器,云计算要求其支持更密集的计算能力(目前多路多核架构),完全的虚拟化能力(CPU指令虚拟化、软件虚拟化、桥片虚拟化、IO虚拟化),多个IO(数据访问与存储)的整合—CEE与FCOE;对于网络,面向100G的性能成为云计算的基本支撑平台,搭建更扁平化、高度可扩展的架构,满足虚拟机算环境的大规模二层无环网络、FCOE/DCE的统一交换、L2-L7的深度高速(10GE级性能)安全集成;对于存储,高速IO将面向10G/16G,高度扩展的虚拟化海量存储,面向统一交换IO整合的FCOE。

%title插图%num
图7 云计算的基础架构要求

4 构建与交付云计算

  不论使用I-P-S的哪一层云计算服务,企业都需要考虑是采用SP的计算资源还是自建云计算资源。从目前运营方式,主要可能有6种方式,如图8所示。

%title插图%num
图8 云计算的服务交付方式

方式一,企业所有,自行运营。这是一种典型的私有云模式,企业自建自用,基础资源在企业数据中心内部,运行维护也由企业自己承担。

方式二,企业所有,运维外包。这也是私有云,但是企业只进行投资建设,而云计算架构的运行维护外包给服务商(也可以是SP),基础资源依然在企业数据中心。

方式三,企业所有,运维外包,外部运行。由企业投资建设私有云,但是云计算架构位于服务商的数据中心内,企业通过网络访问云资源,这是一种物理形体的托管型。

方式四,企业租赁,外部运行,资源独占。由SP构建云计算基础资源,企业只是租用基础资源形成自身业务的虚拟云计算,但是相关物理资源完全由企业独占使用,这是一种虚拟的托管型服务(数据托管)。

方式五,企业租赁,外部运行,资源共享调度。由SP构建,多个企业同时租赁SP的云计算资源,资源的隔离与调度由SP管理,企业只关注自身业务,不同企业在云架构内虚拟化隔离,形成一种共享的私有云模式。

方式六,公共云服务。由SP为企业或个人提供面向互联网的公共服务(如邮箱、即时通信、共享容灾等),云架构与公共网络连接,由SP保证不同企业与用户的数据安全。

从更长远的周期来看,云的形态会不断演化,从孤立的云逐步发展到互联的云,如图9所示。

%title插图%num
图9 云的形态演变

  在云计算建设初期,发展比较快的是公共云,*阶段企业的数据中心依然是传统IT架构,但是面向互联网应用的公共云服务快速发展,不同的ISP会构建各自的云,这些云之间相互孤立,为互联网的不同用户需求集服务(如搜索、邮件等),企业数据中心与公共云之间存在公网互联(企业可能会采用公共云服务);

第二阶段企业开始构建自己的私有云,或租赁SP提供的私有云服务,这一阶段是企业数据中心架构的变化,同时,企业为降低成本,采用公共云服务的业务会增加。

第三阶段,企业为进一步降低IT成本,逐步过渡到采用SP提供的虚拟私有云服务(也可能直接跨过第二阶段到第三阶段),存在企业内部云与外部云的互通,形成混合云模式。

第四阶段,由于成本差异和服务差异,企业会采用不同SP提供的云计算服务,因此,形成了一种不同云之间的互联形态,即互联云。

5 结束语

云计算不仅带来了IT模式的变化,也将引发IT服务的变革,企业和用户只需要关注数据是自己的,而对数据的计算存储方式、效率均采用云的服务来实现和提升,云的供应商则将核心业务重点放在IT架构的运营上,服务将成为下一代IT的核心内容。

java 编写简易的http服务器

java 编写简易的http服务器
想要写一个http服务器,首先得了解http协议。这里仅介绍一下相关的知识。

1、http协议
HTTP请求报文
在请求中,HTTP报文由方法、URI、HTTP版本、HTTP首部字段等部分构成。其中方法、URI、HTTP版本作为首行,然后每个HTTP首部字段为一行,以上内容简称为请求头,如下请求头:

GET /favicon.ico HTTP/1.1 //首行,GET为请求方法,favicon.ico为url链接,1.1版本号
Host: 172.30.67.185:8080 //以下为首部字段行
Connection: keep-alive
Accept: image/webp,image/apng,image/*,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
注:请求报文一般由浏览器(客户端)向服务器发送

HTTP响应报文
在响应中,HTTP报文由HTTP版本、状态码、HTTP首部字段3部分构成,其中HTTP版本、状态码作为首行,然后每个HTTP首部字段为一行,以上内容简称为响应头,如下响应头:

HTTP/1.1 200 OK //首行,1.1为版本号,200为状态码。以下行为首行字段
Content-Type:text/html;charset=utf-8

//空一行后,响应客户端的内容,后面会讲
注:请求报文一般由服务器向浏览器(客户端)发送

2、简单的http服务器
服务器启动程序
首先,编写一个服务器启动程序,我们监听了8080端口,然后得到socket对象,接着new SocketServer(s),然后启动线程。线程池简单看作启动线程的工具就行了(需要深入了解线程池的自行百度)。

public static void main(String[] args) {
ServerSocket server=null;
//线程池,简单来说,就是把线程start()改成ex.execute(),然后可以提高服务器性能
ExecutorService ex=Executors.newFixedThreadPool(20);
try {
//建立服务器监听端口
server=new ServerSocket(8080);
while(true) {
Socket s=server.accept();
//然后把接收到socket传给SocketServer并执行该线程
ex.execute(new SocketServer(s));
}
} catch (IOException e) {
e.printStackTrace();
}
}
接着,因为我们的SocketServer需要做的是做一个http服务器,所以重点来了!怎么接受浏览器(客户端)传来的请求呢?然后又怎么给浏览器(客户端)响应内容呢?

http服务器类
因为浏览器(客户端)的请求方法有GET、POST等,在这里,先写一个响应GET的请求。

思路:

1、打开socket对象的输入流,接收浏览器(客户端)的请求头

2、分析字段内容,判断请求方法、链接

3、打开socket对象的输出流,在输出流里写入响应头

4、空一行,写入响应内容。

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;

public class SocketServer implements Runnable{
Socket s;
//构造方法
public SocketServer(Socket s) {
this.s=s;
}
//线程
public void run(){
//输入输出流
OutputStream os=null;
InputStream in=null;
try
{ //打开socket输入流,并转为BufferedReader流
in=s.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(in));

//接收*行,得到请求路径
String requestHeader;
requestHeader=br.readLine();
int begin = requestHeader.indexOf(“/”)+1;
int end = requestHeader.indexOf(“HTTP/”);
String url=”E:\\sublime文件\\Html文件\\第二天案例\\”;
url=url+requestHeader.substring(begin, end);

//打开socket对象的输出流,写入响应头
os = s.getOutputStream();
os.write(“HTTP/1.1 200 OK\r\n”.getBytes());
os.write(“Content-Type:text/html;charset=utf-8\r\n”.getBytes());
os.write(“\r\n”.getBytes());

//空一行,写入响应内容。
File f=new File(url);
if(f.exists()) //判断请求的文件是否存在
{
FileInputStream fin=new FileInputStream(f);
byte []b=new byte[1024];
int len;
while((len=fin.read(b))!=-1)
{
os.write(b,0,len);
}
}
os.flush();
//如果os流没有关闭的话,浏览器会以为内容还没传输完成,将一直显示不了内容
os.close();

}

}
实现过程非常简单,简单到没有判断浏览器(客户端)的请求方法,直接返回了一个请求路径的文件。

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速