[转载]Web性能优化实践——应用层性能优化

mikel阅读(1096)

[转载]Web性能优化实践——应用层性能优化 – 木子博客 – 博客园.

随着公司项目的进一步推广,用户数量的增加,已经面临着单台服务器不能负载的问题。

这次的优化由于时间关系主要分两步走,首先优化应用层代码以提高单台服务器的负载和吞吐率。之后再进行分表,引入队列、MemCached等分布式应用。

项目背景:这是一个在线竞赛的项目(http://race.gwy.591up.com),在竞赛的时间段内数据库的写入压力很大。

当前问题:1、服务器带宽压力。2、数据库压力。

下图是Web服务器CPU使用率报表。

总体上应用层服务器的CPU使用率不高。

下图是Web服务器带宽报表。

从这个报表可以看到,每块竞赛带宽的占用都会出现一个很高的峰值。服务器是百兆的带宽,理论上可用的最大流量是12.5M,有些时候已经快接近理论的峰值了。

我们再看数据库服务器的带宽报表。

同样的,数据库服务器同样的在竞赛的时间点流量超大,很明显这种情况是不正常的,查询肯定是有问题的。

面对这样的问题,确定了第一期主要做以下的优化。

1、 用flash storage做用户做题断点记录。(做题断点:类似程序断点,用户做到第N题时退出做答,下次进入时依然定位到第N题。)这里原来是用数据库存储的,但 用户每做一题都会执行一条UPDATE语句,而数据库是MySQL的MyISAM引擎,更新操作经常被堵塞。

2、更改系统交卷行为。原来系统在用户做完提交竞赛后,会执行一条UPDATE语句更新用户提交试卷的时间点。同样的这个UPDATE也是在同一时间段内执行,和产品经理沟通后,确认在最后一分钟的时候可以不用再执行这个更新,允许用户的作答时间有1分钟的误差。

3、调整数据库的更新语句为插入语句,这个优化点因为时间问题,推迟到第二个优化阶段再处理。

4、调整应用服务器以支持LVS集群。对当前系统进行分析后,暂时可以不用调整代码直接部署集群,问题是在多台服务器内都会存在相同的进程内缓存,这种情况暂时是可以接受的,后期需要改到MemCached集中管理缓存。

5、 等待成绩页面同一时间跳转的压力问题。在线竞赛的提交时间点很集中,用户做答完题目后,会统一跳转到一个页面等待答案(这时后台的Windows 服务在进行竞赛统计),这里服务器的并发、带宽压力都非常大。因此,优化这里不进行跳转,而是在当前的页面等待,并且会自动给不同的用户分配不同的等待时 间,以避免占满服务器的带宽。

6、 每场完整的公务员考试试卷,题目资源有150K-200K,因为作答和查看解析是在不同的页面,之前的实现会造成题目的多次加载,严重的浪费了带宽资源。 于是这里优化成Handler输入静态资源加载,从服务器加载一次之后,后面所有的地方调用到题目都可以从浏览器的本地缓存中加载带省服务器带宽。同时, 服务器上只对静态资源服务器开启了GZip压缩,对动态文件进行压缩会浪费服务器的CPU资源,而只对Handler输出的题目进行GZip压缩,一方面 节省了CPU,另一方面把150K-200K的题目资源压缩到了50K左右。

7、数据库性能优化。调整了代码中查询的各个条件的位置,使查询语句能够更多的使用到索引。同时把原来每次一条的插入操作修改为一次插入多条等一些数据库查询优化。

任何一个优化都要针对已经存在的问题,从服务器监控的报表可以看到我们这个网站应用服务器带宽压力、数据库服务器带宽压力都很大,应用服务器的CPU使用率不高,因此,主要的优化是对应用服务器带宽和数据库服务器的写入压力做的优化,因为目的很明确,效果也是比较明显的。

文中提到了用Handler来输出静态资源让浏览器缓存,附上这个代码,其它的优化针对性很高,就不再啰嗦了,主要的还是记录下这次优化的工作方式和工作方法。

Handler输出的静态资源使用了.NET流压缩,于是我们声明一个压缩器接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System.IO;
namespace ND.Race.Compressor
{
/// <summary>
/// 压缩器接口
/// </summary>
public interface ICompressor
{
string EncodingName { get; }
bool CanHandle(string acceptEncoding);
void Compress(string content, Stream targetStream);
}
}

GZip压缩器实现这个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using System.IO;
using System.IO.Compression;
using System.Text;
namespace ND.Race.Compressor
{
public sealed class GZipCompressor : ICompressor
{
public string EncodingName
{
get { return "gzip"; }
}
public bool CanHandle(string acceptEncoding)
{
return !string.IsNullOrEmpty(acceptEncoding) &&
acceptEncoding.Contains("gzip");
}
public void Compress(string content, Stream targetStream)
{
using (var writer = new GZipStream(targetStream, CompressionMode.Compress))
{
var bytes = Encoding.UTF8.GetBytes(content);
writer.Write(bytes, 0, bytes.Length);
}
}
}
}

同样的Deflate压缩器也实现这个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using System.IO;
using System.IO.Compression;
using System.Text;
namespace ND.Race.Compressor
{
public sealed class DeflateCompressor : ICompressor
{
public string EncodingName
{
get { return "deflate"; }
}
public bool CanHandle(string acceptEncoding)
{
return !string.IsNullOrEmpty(acceptEncoding) &&
acceptEncoding.Contains("deflate");
}
public void Compress(string content, Stream targetStream)
{
using (var writer = new DeflateStream(targetStream, CompressionMode.Compress))
{
var bytes = Encoding.UTF8.GetBytes(content);
writer.Write(bytes, 0, bytes.Length);
}
}
}
}

如果浏览器不支持流压缩,那我们只能直接输出内容了,因此我们还需要一个不进行压缩的处理类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System.IO;
using System.Text;
namespace ND.Race.Compressor
{
public sealed class NullCompressor : ICompressor
{
public string EncodingName
{
get { return "utf-8"; }
}
public bool CanHandle(string acceptEncoding)
{
return true;
}
public void Compress(string content, Stream targetStream)
{
using (targetStream)
{
var bytes = Encoding.UTF8.GetBytes(content);
targetStream.Write(bytes, 0, bytes.Length);
}
}
}
}

现在我们就可以开始编码我们的Handler了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
public class QuestionCacheHandler : IHttpHandler
{
#region 静态变量
/// <summary>
/// 资源过期时间
/// </summary>
private static readonly int durationInDays = 30;
/// <summary>
/// 流压缩接口
/// </summary>
private static readonly ICompressor[] Compressors = {
new GZipCompressor(),
new DeflateCompressor(),
new NullCompressor()
};
#endregion
#region 私有变量
/// <summary>
/// 内存流压缩类
/// </summary>
private ICompressor compressor;
/// <summary>
/// ETAG
/// </summary>
private string eTagCacheKey;
/// <summary>
/// 竞赛场次Id
/// </summary>
private long raceId;
#endregion
public void ProcessRequest(HttpContext context)
{
if (context == null) return;
long.TryParse(context.Request.QueryString["raceId"], out raceId);
if (raceId == 0) return;
var acceptEncoding = context.Request.Headers["Accept-Encoding"];
compressor = Compressors.First(o => o.CanHandle(acceptEncoding));
eTagCacheKey = string.Concat(raceId, "/etag");
if (IsInBrowserCache(context, eTagCacheKey)) return;
SendOutputToClient(context, true, eTagCacheKey);
}
/// <summary>
/// 发送内容到客户端
/// </summary>
/// <param name="context"></param>
/// <param name="insertCacheHeaders"></param>
/// <param name="etag"></param>
private void SendOutputToClient(HttpContext context, bool insertCacheHeaders, string etag)
{
string content = "";
MemoryStream memoryStream = new MemoryStream();
compressor.Compress(content, memoryStream);
byte[] bytes = memoryStream.ToArray();
HttpResponse response = context.Response;
if (insertCacheHeaders)
{
HttpCachePolicy cache = context.Response.Cache;
cache.SetETag(etag);
cache.SetOmitVaryStar(true);
cache.SetMaxAge(TimeSpan.FromDays(durationInDays));
cache.SetLastModified(DateTime.Now);
cache.SetExpires(DateTime.Now.AddDays(durationInDays)); // HTTP 1.0 的浏览器使用过期时间
cache.SetValidUntilExpires(true);
cache.SetCacheability(HttpCacheability.Public);
cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
cache.VaryByHeaders["Accept-Encoding"] = true;
}
response.AppendHeader("Content-Length", bytes.Length.ToString(System.Globalization.CultureInfo.InvariantCulture));
response.ContentType = "text/plain";
response.ContentType = "application/x-JavaScript";
response.AppendHeader("Content-Encoding", compressor.EncodingName);
if (bytes.Length > 0)
response.OutputStream.Write(bytes, 0, bytes.Length);
if (response.IsClientConnected)
response.Flush();
}
/// <summary>
/// 是否浏览器已经缓存
/// </summary>
/// <param name="context"></param>
/// <param name="etag"></param>
/// <returns></returns>
private bool IsInBrowserCache(HttpContext context, string etag)
{
string incomingEtag = context.Request.Headers["If-None-Match"];
if (String.Equals(incomingEtag, etag, StringComparison.Ordinal))
{
context.Response.Cache.SetETag(etag);
context.Response.AppendHeader("Content-Length", "0");
context.Response.StatusCode = (int)System.Net.HttpStatusCode.NotModified;
context.Response.End();
return true;
}
return false;
}
public bool IsReusable
{
get
{
return false;
}
}
}

服务器端代码通过Http请求Header的Accept-Encoding来判断是否支持流压缩,再通过Header的etag来判断浏览器中是否已经有缓存副本。

关注更多相关内容,请移步:  http://blog.moozi.net/

[转载]Google Local Search API 简介

mikel阅读(1289)

[转载]Google Local Search API 简介 – liongis – 博客园.

Google 提供了一个基于JavaScript的本地搜索的API,我们可以通过这个API来嵌入到我们的应用程序中,实现搜索的功能。如javascrtip,Flash,java等。

此接口返回的数据为JSON格式的数据,可以方便进行解析。

Google Local Search API首页地址是:

http://code.google.com/intl/zh-CN/apis/maps/documentation/localsearch/index.html

以下是一个简单的例子:

1 <DOCTYPE html> 2  <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta http-equiv="content-type" content="text/html; charset=utf-8"/> 5 <title>Google Search API Sample</title> 6 <script src="http://www.google.com/jsapi?key=INSERT-YOUR-KEY"></script> 7 <script type="text/javascript"> 8 // This code generates a "Raw Searcher" to handle search queries. The Raw Searcher requires 9   // you to handle and draw the search results manually. 10   google.load('search', '1'); 11 12 var localSearch; 13 function searchComplete() { 14 15 // Check that we got results 16 document.getElementById('content').innerHTML = ''; 17 if (localSearch.results && localSearch.results.length > 0) { 18 for (var i = 0; i < localSearch.results.length; i++) { 19 20 // Create HTML elements for search results 21 var p = document.createElement('p'); 22 var a = document.createElement('a'); 23 var b = document.createElement('b'); 24 var c = document.createElement('c'); 25 a.href = localSearch.results[i].url; 26 a.innerHTML = localSearch.results[i].title; 27 b.innerHTML = "<br>" + 28 localSearch.results[i].streetAddress; 29 c.innerHTML = "<br>" + 30 localSearch.results[i].city + "," + 31 localSearch.results[i].region; 32 33 // Append search results to the HTML nodes 34 p.appendChild(a); 35 p.appendChild(b); 36 p.appendChild(c); 37 document.body.appendChild(p); 38 } 39 } 40 } 41 42 function onLoad() { 43 44 // Create a LocalSearch instance. 45 localSearch = new google.search.LocalSearch(); 46 47 // Set the Local Search center point 48 localSearch.setCenterPoint("New York, NY"); 49 50 // Set searchComplete as the callback function when a search is complete. The 51 // localSearch object will have results in it. 52 localSearch.setSearchCompleteCallback(this, searchComplete, null); 53 54 // Specify search quer(ies) 55 localSearch.execute('coffee New York NY'); 56 57 // Include the required Google branding. 58 // Note that getBranding is called on google.search.Search 59 google.search.Search.getBranding('branding'); 60 } 61 62 // Set a callback to call your code when the page loads 63 google.setOnLoadCallback(onLoad); 64 65 </script> 66 </head> 67 <body style="font-family: Arial;border: 0 none;"> 68 <div id="branding" style="float: left;"></div><br /> 69 <div id="content">Loading...</div> 70 </body> 71 </html>

其中最重要的是调用这个地址:

http://ajax.googleapis.com/ajax/services/search/local?v=1.0&q=Palm%20Springs%20CA
两个必须的参数如下:
v:版本号,如1.0
q:搜索的关键字
还有一些其它常可以用到的参数:
key:搜索的时候,需要验证的key值,这个你必须到google上去申请
sll:中心坐标,你可以指定一个坐标为中心进行搜索
rsz:每页显示几条数据,值为1-8,当然,每次搜索最大记录数为64

我们来看看常见的几种语言是如何来使用的:
使用Flash
var service:HTTPService = new HTTPService(); service.url = 'http://ajax.googleapis.com/ajax/services/search/local'; service.request.v = '1.0'; service.request.q = 'Palm%20Springs%20CA'; service.request.key = 'INSERT-YOUR-KEY'; service.resultFormat = 'text'; service.addEventListener(ResultEvent.RESULT, onServerResponse); service.send(); private function onServerResponse(event:ResultEvent):void { try { var json:Object = JSON.decode(event.result as String); // now have some fun with the results... } catch(ignored:Error) { } }
使用Java

URL url = new URL("http://ajax.googleapis.com/ajax/services/search/local?" + "v=1.0&q=barack%20obama&key=INSERT-YOUR-KEY&userip=INSERT-USER-IP"); URLConnection connection = url.openConnection(); connection.addRequestProperty("Referer", /* Enter the URL of your site here */); String line; StringBuilder builder = new StringBuilder(); BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); while((line = reader.readLine()) != null) { builder.append(line); } JSONObject json = new JSONObject(builder.toString()); // now have some fun with the results...

使用PHP

$url = "http://ajax.googleapis.com/ajax/services/search/local?" + "v=1.0&q=barack%20obama&key=INSERT-YOUR-KEY&userip=INSERT-USER-IP"; // sendRequest // note how referer is set manually $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_REFERER, /* Enter the URL of your site here */); $body = curl_exec($ch); curl_close($ch); // now, process the JSON string $json = json_decode($body); // now have some fun with the results...


今天先介绍到这里,以后我会更详细的进行简介一下。

下面是我用Flex做的一个示例,结合了Google Map,支持关键字搜索,并可以导出结果。

再次申明,程序只用于学习使用,请必用于商业。需要安装Flash AIR

下载地址是:http://files.cnblogs.com/liongis/GMapLocalSearch.rar

[转载]html5声频audio和视频video

mikel阅读(1179)

[转载]html5声频audio和视频video – 破狼 – 博客园.

html5作为下一代web标准,年前轩起了html5热潮。对于html5我只是本着了解看看。关于html5和 RIA(silverlight,flash,JavaFx等)我不想说什么,也没有什么可说的,存在就有其存在的理由。孰优孰劣,留给事实、时间来证明 的。

在html5中出现了一些新特性:

  • canvas 元素
  • 视频 video 和 声频audio 元素 ;
  • 对本地离线存储(localStorage,sessionStorage)的支持 ;
  • 新增特殊内容元素:article、footer、header、nav、section ;
  • 新增表单控件: calendar、date、time、email、url、search 。

今天看看视频和声频:在html5中规定了视频的标准方法:video

<video src="xxx.ogg" controls="controls">你的浏览器还不支持哦</video>
  我们也可以设置多个source,浏览器会为我们选择第一个可识别的视频来播放,形如:
<video width="320" height="240" controls="controls">
<source src="xxx.ogg" type="video/ogg">
 <source src="xx1.mp4" type="video/mp4">
你的浏览器还不支持哦</video>
video属性有:
属性 描述
autoplay autoplay 如果出现该属性,则视频在就绪后马上播放。
controls controls 如果出现该属性,则向用户显示控件,比如播放按钮。
height 像素 设置视频播放器的高度。
loop loop 如果出现该属性,则当媒介文件完成播放后再次开始播放。
preload preload 如果出现该属性,则视频在页面加载时进行加载,并预备播放。

如果使用 “autoplay”,则忽略该属性。

src url视频地址 要播放的视频的 URL。
width 像素 设置视频播放器的宽度。
autobuffer Autobuffer

(自动缓冲)

在网页显示时,该二进制属性表示是由用户代理(浏览器)自动缓冲的内容,还是由用户使用相关API进行内容缓冲
poster url图片地址

当视频未响应或缓冲不足时,该属性值链接到一个图像。该图像将以一定比例被显示出来

在HTML5 规定了声频标准为 audio 元素,audio 元素能够播放声音文件或者音频流。

audio格式和video相似:直接行多source:

<audio controls="controls">
  <source src="xx.ogg" type="audio/ogg">
  <source src="xx1.mp3" type="audio/mpeg">
你的浏览器还不支持哦
</audio>
其属性比video少了height、width、poster。
在我们的开发中多媒体越来越重要,html5出现了这些video和audio。

[转载]介绍一个开源的ORM--ORM.NET

mikel阅读(825)

[转载]介绍一个开源的ORM–ORM.NET – Code,I hate you… – 博客园.

一.介绍

ORM.NET是一个开源的.net对象关系映射的代码生成工具,它根据数据库结构生成一个数据实体层,为每一个表生成一个对应的类,为表的每一个字段生成类的一个属性。

通过生成的DataManager对象,不用使用存储过程和嵌入SQL脚本就可以很容易根据复杂的条件检索数据,除此之外,数据更新,插入和删除可以通过一次调用就可以对数据进行保存。

官网地址:http://orm-net.sourceforge.net/

下载:http://sourceforge.net/projects/orm-net/files/ORM.NET/

二.演示

http://orm-net.sourceforge.net/tour.html

三.官方提供的使用示例

//根据数据库连接字符串创建一个数据管理对象

DataManager data = new DataManager(Config.Dsn);

//获取一个Customers集合并检索关联的Order表

CustomersCollection customers = data.GetCustomersCollection(FetchPath.Customers.Orders);

//根据ContactName进行降序排序

customers.SortByContactName(SortDirection.Descending);

//更新Customers表中CustomerID=”ALFKI”的记录,把ContactName修改为”Peach”

customers.FindByCusmerID(“ALFKI”).ContactName = “Peach”;

//创建一个新的customer对象

Customers customer = data.NewCustomers(“Tes7″,”Orero software”);

customer.ContactName = “Tech Support”;

//创建新的关联对象

Orders order = customer.NewOrders();

order.OrderDate = DateTime.Now;

//通知DataManager更新数据库一直持续到所有的操作完成

data.CommitAll();

说明:我只是翻译过来跟大家分享一下这个ORM,让大家多了解一下,具体我没在项目中用过也没测试过,个人觉得这样的ORM只适合做网站和小项目可能提高开发效率。

[转载]AndroidNDK开发之“文件操作”

mikel阅读(1019)

[转载]AndroidNDK开发之“文件操作” – duicky – 博客园.

其实和上层没什么关系,主要是通过C来完成文件的基本操作。不好意思大家,时间不够,不多说,贴上关键代码。

关键文件代码:

MainActivity.java

package com.scan.file;

import Android.app.Activity;
import Android.os.Bundle;
import Android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {

private static final String TAG = “File”;
private Button doc = null;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

initControls();
}

/**
* 初始化控件
*/
public void initControls() {
doc = (Button) this.findViewById(R.id.btn_do_c);
doc.setOnClickListener(new MyButtonOnClickListener());
}

/**
* 监听ButtonOnClick
*
* @author lxf
*
*/
class MyButtonOnClickListener implements OnClickListener {

@Override
public void onClick(View v) {
switch (v.getId()) {

case R.id.btn_do_c:
String do_c_result = doCMethod();
displayMessage(do_c_result);
break;
}

}

}

/*
* Toast显示消息
*/
private void displayMessage(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}

/**
* 测试方法
*
* @return
*/
public native String sayHello();

/**
* 执行C底层的方法
*
* @return
*/
public native String doCMethod();

/*
* 载人本地库文件
*/
static {
System.loadLibrary(“AndroidJni”);
}
}

file.h

#include <string.h>
#include <stdio.h>
#include “define.h”

unsigned short File_Open(FILE_HANDLE** FileHandle, char* name, unsigned short flag,unsigned short mode);

unsigned short File_Close(FILE_HANDLE* FileHandle);

unsigned short File_GetSize(FILE_HANDLE* FileHandle, unsigned long* FileSize);

unsigned short File_Read(FILE_HANDLE* FileHandle, char* buf, unsigned long count,unsigned long* ReadCount);

unsigned short File_Write(FILE_HANDLE* FileHandle, char* buf, unsigned long count,unsigned long* WriteCount);

unsigned short File_Seek(FILE_HANDLE* FileHandle, long offset, short origin,unsigned long* SeekLen);

unsigned short File_Delete(char* name);

unsigned short File_ISExist(char* path);

unsigned short File_Create_Dir(char* dirName);

unsigned short File_Delete_Dir(char* dirName);

Android.mk
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := AndroidJni
LOCAL_SRC_FILES := AndroidJni.c File.c SyncmlEngine.c

LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog

include $(BUILD_SHARED_LIBRARY)

AndroidJni.c

#include <string.h>
#include <jni.h>
#include <android/log.h>
#include “SyncmlEngine.h”

//测试方法sayHello
jstring Java_com_scan_file_MainActivity_sayHello(JNIEnv* env, jobject thiz) {
//打印信息出来
__android_log_print(ANDROID_LOG_INFO, “JNIMsg”, “SayHello”);
return (*env)->NewStringUTF(env, “Hello from JNI ! sayHello”);
}

//执行C底层方法
jstring Java_com_scan_file_MainActivity_doCMethod(JNIEnv* env, jobject thiz) {
//打印信息出来
__android_log_print(ANDROID_LOG_INFO, “JNIMsg”, “doCMethod”);

SyncmlStart();

return (*env)->NewStringUTF(env, “Do C Method OK!”);
}

file.c

#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <dirent.h>
#include <android/log.h>
#include “File.h”

/**
* File_Open–打开文件
* flag–读\写\读写\追加
* mode–如果没有,是否新建
*/
unsigned short File_Open(FILE_HANDLE** FileHandle, char* name,
unsigned short flag, unsigned short mode) {

if (FileHandle == NULL) {
return 100;
}
if (name == NULL) {
return 100;
}
char type[128] = “”;

if (flag == FO_OREAD) {
if (mode != FO_CREATE) {
strcpy(type, “r”); //只读,不建文件
} else {
strcpy(type, “r+”);
}
} else if (flag == FO_OWRITE) {
if (mode != FO_CREATE) {
strcpy(type, “w”); //只写,不建文件
} else {
strcpy(type, “w+”);
}
} else if (flag == FO_RW) {
if (mode != FO_CREATE) {
strcpy(type, “a”);
} else {
strcpy(type, “a+”);
}
} else if (flag == FO_APPEND) {
if (mode != FO_CREATE) {
strcpy(type, “a”);
} else {
strcpy(type, “a+”);
}
}

*FileHandle = fopen(name, type);
return 0;
}

/**
* File_Close–关闭文件
* 返回值
* 0–关闭成功;否则失败
*/
unsigned short File_Close(FILE_HANDLE* FileHandle) {
if (FileHandle == NULL) {
return 100;
}
return fclose(FileHandle);
}

/**
* File_GetSize–得到文件长度
* FileSize–返回文件长度
*/
unsigned short File_GetSize(FILE_HANDLE* FileHandle, unsigned long* FileSize) {
if (FileHandle == NULL) {
return 100;
}
fseek(FileHandle, 0L, SEEK_END);
*FileSize = ftell(FileHandle);
return 0;
}

/**
* File_Read–读取文件到buf
* count–读取的长度
* ReadCount–返回已读取的长度
*/
unsigned short File_Read(FILE_HANDLE* FileHandle, char* buf,
unsigned long count, unsigned long* ReadCount) {
if (FileHandle == NULL) {
return 100;
}
*ReadCount = fread(buf, 1, count, FileHandle);
__android_log_print(ANDROID_LOG_INFO, “JNIMsg”,
“File_Read           ReadCount=%d”, *ReadCount);
return 0;
}

/**
* File_Write–从buf中写入文件
* count–写入的长度
* WriteCount–返回已写入的长度
*/
unsigned short File_Write(FILE_HANDLE* FileHandle, char* buf,
unsigned long count, unsigned long* WriteCount) {
if (FileHandle == NULL) {
return 100;
}
unsigned short write_result = fwrite(buf, count, 1, FileHandle); // 返回值是成功写入的项目数
if(write_result == 1) {
*WriteCount = write_result * count;
}
return write_result;
}

/**
* File_Seek–给文件偏移量
* offset–偏移量
* origin–偏移方向
* SeekLen–返回已偏移的长度
* 返回值0–成功,其他失败
*/
unsigned short File_Seek(FILE_HANDLE* FileHandle, long offset, short origin,
unsigned long* SeekLen) {
unsigned short seek_result = fseek(FileHandle, offset, origin);
if(seek_result == 0) {
*SeekLen = offset;
}
return seek_result;
}

/**
* File_Delete–删除文件
* 0–删除成功  -1–删除失败
*/
unsigned short File_Delete(char* name) {
if (name == NULL) {
return 100;
}
return remove(name);
}

/**
* File_ISExist–判断文件是否存在
* 0存在  -1不存在
*/
unsigned short File_ISExist(char* path) {
if (path == NULL) {
return 100;
}
return access(path, 0);
}

/**
* File_Create_Dir–创建文件目录
* 返回0–成功
*/
unsigned short File_Create_Dir(char* dirName) {
if (dirName == NULL) {
return 100;
}
return mkdir(dirName, S_IRWXU);
}

/**
* File_Delete_Dir–删除文件目录
*/
unsigned short File_Delete_Dir(char* dirName) {
if (dirName == NULL) {
return 100;
}
DIR* dp = NULL;
DIR* dpin = NULL;
char *pathname = (char*) malloc(256);
memset(pathname, 0, 256);
struct dirent* dirp;
dp = opendir(dirName);
if (dp == NULL) {
__android_log_print(ANDROID_LOG_INFO, “JNIMsg”,
“File_Delete_Dir      your input directory is not exist!”);
return 100;
}
while ((dirp = readdir(dp)) != NULL) {
if (strcmp(dirp->d_name, “..”) == 0 || strcmp(dirp->d_name, “.”) == 0)
continue;
strcpy(pathname, dirName);
strcat(pathname, “/”);
strcat(pathname, dirp->d_name);
dpin = opendir(pathname);
if (dpin != NULL) {
closedir(dpin);
dpin = NULL;
File_Delete_Dir(pathname);
} else {
remove(pathname);
}
}
rmdir(dirName);
closedir(dp);
free(pathname);
pathname = NULL;
dirp = NULL;

return 0;
}

有看不懂的地方可以留言,一起探讨。

[转载]中兴 X876使用技巧

mikel阅读(1125)

X876使用小技巧

第一次使用安卓系统,于是把自己遇到并解决的问题一个个写出来,都是在876上实践的。

1.关于WIFI黑屏就掉线的解决方法:
进入“设置”-“无线和网络”-“WLAN设置”-“MENU(手机的菜单键)”-“高级”-“WLAN休眠策略”-“永不休眠”-“MENU”-“保存”
有很多人说无效,我也不知道什么原因,之前我是没问题的,后来也不行了~郁闷。

2.蓝牙接发问题:
发送:点住文件不放,弹出对话框,选蓝牙发送。
接收:收到消息时,(注意看手机最左上角的消息通知),然后试试按住信息栏不放往下托拉。(信息栏就是最上面那栏,有信号阿,电池阿这里)

3.图标的显示、隐藏和换屏:
显示:有的人安装了软件后,软件不会在主屏上显示,而是直接到了程序库里(自己暂时这么叫了),调出来的方法是进入程序库,找到对应软件的图标点住不放直到可移动后直接移到主屏上。
隐藏:在主屏上对着一个图标点住不放直到可移动后(有震动提示),把它扔到垃圾桶。
换屏:在主屏上对着一个图标点住不放直到可移动后,移到主屏的边缘不动,手机自动会换屏。
程序库进入方法:激活屏幕后,MENU对上的那个点阵块。

4.SD装载与卸载:
手机用数据线连上电脑后,手机方会有提示“装载”或“不装载”,装载就是连上SD卡,这时可以拷贝文件到手机SD卡上。
如果要断开SD与电脑的连接,直接按住信息栏不放往下托拉,点击“关闭USB存储设备”,再点“关”闭即可。
如果一开始点了不装载,之后又想装载。直接按住信息栏不放往下托拉,点击“USB已连接”再点“装载”即可
注:以上“4”的操作从没断开过数据线。(这个好阿,手机屁屁可以多插几年了)

5.输入法的切换:
很多人不喜欢手机自带的输入法,包括我也一样。所以会自行装其他输入法(我最喜欢百度输入法),那么要如何在输入的时候换你安装好的输入法呢?
方法:点住输入框不放。然后看到了吧?

6.google搜索框的调用:
其实手机带的google搜索很有用,很方便。输入一个字符,手机有关的就会显示出来,无关的也可以调出来。不过它占在主屏上很不好看也浪费位置。我们可以用上面“3”的方法把它去掉。那么要如何调出来呢?
激活屏幕,然后对着“MENU”长按不放,看到什么了没?一个键盘! 直接输入就行了。比如输入“U” 那么”UC浏览器”是不是就可以用了呢?  再比如输入“www.baidu.com”是不是直接就可以用UC上网呢?(前提是UC是默认浏览器)就不用特意去开UC,开了UC后又输入了。方便吧?

7.后台程序的查看:
方法跟“6”一样,不过是对着房子键长按。(MENU左边那个)。不过好像不能结束程序。
下面8楼的兄弟”cby200311″指的就是这个。加分鼓励。

8.长按屏幕左下或右下的“…”键,能够调出五屏来。

9.壁纸不能全屏?
解决方法:别用240X400的壁纸(虽然像素是这个),876的壁纸是跟着屏幕动的,876有五个分屏!经过试验。  请使用像素为:600X500的壁纸。 我已经验证可行,相当不错。

10.铃音、信息音和闹钟音的自由调用:
铃音:找到声音文件,长按不放,弹出的对话框中选“用作铃声
信息音:进入信息-“MENU”-“设置”-“选择铃声” 确定即可。
闹钟音:进入“闹钟时钟”–“MENU”-“添加闹钟”(或对着已有闹钟长按-编辑)-“铃声” 选择确定即可。

自由铃音:
在SD卡的根目录下增加media文件夹,在该文件夹里创建audio文件夹,在该文件夹里在创建以下文件夹:
ringtones      文件夹—来电铃声,里面放入自选的铃声音乐
notifications  文件夹—短信铃声,里面放入自选的铃声音乐;
alarms          文件夹—闹铃铃声,里面放入自选的铃声音乐;
设置好后,用上面的方法进行调用。

[转载]深入理解Android消息处理系统——Looper、Handler、Thread

mikel阅读(859)

[转载]深入理解Android消息处理系统——Looper、Handler、Thread – Greenwood – 博客园.

(自) Activity,Service属于主线程,在主线程中才能更新UI,如toast等。其他线程中不能直接使用,这时可以使用Handler来处理,Handler可以在Activity和Service中。

熟悉Windows编程的朋友可能知道Windows程序是消息驱动的,并且有全局的消息循环系统。而Android应用程序也是消息驱动的,按道 理来说也应该提供消息循环机制。实际上谷歌参考了Windows的消息循环机制,也在Android系统中实现了消息循环机制。Android通过 Looper、Handler来实现消息循环机制,Android消息循环是针对线程的(每个线程都可以有自己的消息队列和消息循环)。本文深入介绍一下 Android消息处理系统原理。

Android系统中Looper负责管理线程的消息队列和消息循环,具体实现请参考Looper的源码。 可以通过Loop.myLooper()得到当前线程的Looper对象,通过Loop.getMainLooper()可以获得当前进程的主线程的 Looper对象。

前面提到Android系统的消息队列和消息循环都是针对具体线程的,一个线程可以存在(当然也可以不存在)一个消息队列和一个消息循环 (Looper),特定线程的消息只能分发给本线程,不能进行跨线程,跨进程通讯。但是创建的工作线程默认是没有消息循环和消息队列的,如果想让该线程具 有消息队列和消息循环,需要在线程中首先调用Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环。如下例 所示:

class LooperThread extends Thread {
public Handler mHandler;

public void run() {
Looper.prepare();

mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};

Looper.loop();
}
}这样你的线程就具有了消息处理机制了,在Handler中进行消息处理。

Activity是一个UI线程,运行于主线程中,Android系统在启动的时候会为Activity创建一个消息队列和消息循环(Looper)。详细实现请参考ActivityThread.java文件。

Handler的作用是把消息加入特定的(Looper)消息队列中,并分发和处理该消息队列中的消息。构造Handler的时候可以指定一个Looper对象,如果不指定则利用当前线程的Looper创建。详细实现请参考Looper的源码。

Activity、Looper、Handler的关系如下图所示:

message

一个Activity中可以创建多个工作线程或者其他的组件,如果这些线程或者组件把他们的消息放入Activity的主线程消息队列,那么该消息就会在 主线程中处理了。因为主线程一般负责界面的更新操作,并且Android系统中的weget不是线程安全的,所以这种方式可以很好的实现Android界 面更新。在Android系统中这种方式有着广泛的运用。

那么另外一个线程怎样把消息放入主线程的消息队列呢?答案是通过Handle对象,只要Handler对象以主线程的Looper创建,那么调用 Handler的sendMessage等接口,将会把消息放入队列都将是放入主线程的消息队列。并且将会在Handler主线程中调用该handler 的handleMessage接口来处理消息。

这里面涉及到线程同步问题,请先参考如下例子来理解Handler对象的线程模型:

1、首先创建MyHandler工程。

2、在MyHandler.java中加入如下的代码:

package com.simon;

import android.app.Activity;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.os.Handler;

public class MyHandler extends Activity {
static final String TAG = “Handler”;
Handler h = new Handler(){
public void handleMessage (Message msg)
{
switch(msg.what)
{
case HANDLER_TEST:
Log.d(TAG, “The handler thread id = ” + Thread.currentThread().getId() + “\n”);
break;
}
}
};

static final int HANDLER_TEST = 1;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, “The main thread id = ” + Thread.currentThread().getId() + “\n”);

new myThread().start();
setContentView(R.layout.main);
}

class myThread extends Thread
{
public void run()
{
Message msg = new Message();
msg.what = HANDLER_TEST;
h.sendMessage(msg);
Log.d(TAG, “The worker thread id = ” + Thread.currentThread().getId() + “\n”);
}
}
}在这个例子中我们主要是打印,这种处理机制各个模块的所处的线程情况。如下是我的机器运行结果:

09-10 23:40:51.478: Debug/Handler(302): The main thread id = 1
09-10 23:40:51.569: Debug/Handler(302): The worker thread id = 8
09-10 23:40:52.128: Debug/Handler(302): The handler thread id = 1我们可以看出消息处理是在主线程中处理的,在消息处理函数中可以安全的调用主线程中的任何资源,包括刷新界面。工作线程和主线程运行在不同的线程中,所 以必须要注意这两个线程间的竞争关系。

上例中,你可能注意到在工作线程中访问了主线程handler对象,并在调用handler的对象向消息队列加入了一个消息。这个过程中会不会出现消息队 列数据不一致问题呢?答案是handler对象不会出问题,因为handler对象管理的Looper对象是线程安全的,不管是加入消息到消息队列和从队 列读出消息都是有同步对象保护的,具体请参考Looper.java文件。上例中没有修改handler对象,所以handler对象不可能会出现数据不 一致的问题。

通过上面的分析,我们可以得出如下结论:

1、如果通过工作线程刷新界面,推荐使用handler对象来实现。

2、注意工作线程和主线程之间的竞争关系。推荐handler对象在主线程中构造完成(并且启动工作线程之后不要再修改之,否则会出现数据不一致),然后在工作线程中可以放心的调用发送消息SendMessage等接口。

3、除了2所述的hanlder对象之外的任何主线程的成员变量如果在工作线程中调用,仔细考虑线程同步问题。如果有必要需要加入同步对象保护该变量。

4、handler对象的handleMessage接口将会在主线程中调用。在这个函数可以放心的调用主线程中任何变量和函数,进而完成更新UI的任务。

5、Android很多API也利用Handler这种线程特性,作为一种回调函数的变种,来通知调用者。这样Android框架就可以在其线程中将消息发送到调用者的线程消息队列之中,不用担心线程同步的问题。

深入理解Android消息处理机制对于应用程序开发非常重要,也可以让你对线程同步有更加深刻的认识。以上是最近Simon学习Android消息处理机制的一点儿总结,如有错误之处请不吝指教。

转自:http://blog.csdn.net/dywe_ddm/archive/2010/10/10/5930948.aspx

[转载]Flash游戏编程指南

mikel阅读(1106)

[转载]Flash游戏编程指南-RIABook.cn.


这本是天地会译林军翻译的完整版,700多页真是辛苦了,除了每页上边的培训广告有点煞风景其他都好.
英文原书是这本The Essential Guide to Flash Games: Building Interactive Entertainment with ActionScript

目录

第一部分  基本游戏框架
第一章 第二游戏说          (译者:aserrewin)
第二章 创建一个AS3游戏框架     (译者:aserrewin)
第三章 创建超级点击         (译者:kenjor)

第二部分  游戏实例
第四章 御空加农炮的基础架构     (译者:yyluo-阿树)
第五章 构建御空加农炮游戏循环    (译者:yyluo-阿树)
第六章 预备!坦克大战        (译者:sun11086-0025)
第七章 构建坦克大战游戏       (译者:sun11086-0025,peichao01)
第八章 休闲智力游戏-魔法色块    (译者:cosmos53076)
第九章 骰子游戏王          (译者:yangjh415)
第十章 滚屏游戏世界         (译者:Pizzaman)
第十一章 制作绝佳的反应力游戏    (译者:享受生活)
第十二章 制作一个Viral Game: 隧道惊魂 (译者:心月不皈,Mr.Star )

下载:

From 115网盘
From 微盘
From 天地会

[转载]android开发我的新浪微博客户端-阅读微博功能篇(6.2)

mikel阅读(878)

[转载]android开发我的新浪微博客户端-阅读微博功能篇(6.2) – 遇见未知的自己 – 博客园.

注:最近由于OAuth上传图片碰到了难题,一直在做这方面的研究导致博客很久没有更新。

在上面一篇中已经实现了预读微博的UI界面,效果如上图,接下来完成功能部分的代码,当用户在上一个列表界面的列表中点击某一条微博的时候显示这个阅读微博的界面,在这个界面中根据传来的微博ID,然后根据这个ID通过api获取微博的具体内容进行显示。

在ViewActivity.class的onCreate方法中添加如下代码:

private UserInfo user;
private String key=“”;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view);

。。。。。

//获取上一个页面传递过来的key,key为某一条微博的id
Intent i=this.getIntent();
if(!i.equals(null)){
Bundle b
=i.getExtras();
if(b!=null){
if(b.containsKey(key)){
key
= b.getString(key);
view(key);
}
}
}

}

接下来就是view方法具体获取微博内容的方法,在这个方法中如果获取的本条微博如果包含图片那么就用前面AsyncImageLoader的方法异步载入图片并且进行显示,同时在这个方法中还要获取本条微博被转发的次数以及评论的次数,具体代码如下:

private void view(String id){
user
=ConfigHelper.nowUser;
OAuth auth
=new OAuth();
String url
= http://api.t.sina.com.cn/statuses/show/:id.json;
List params
=new ArrayList();
params.add(
new BasicNameValuePair(source, auth.consumerKey));
params.add(
new BasicNameValuePair(id, id));
HttpResponse response
=auth.SignRequest(user.getToken(), user.getTokenSecret(), url, params);
if (200 == response.getStatusLine().getStatusCode()){
try {
InputStream is
= response.getEntity().getContent();
Reader reader
= new BufferedReader(new InputStreamReader(is), 4000);
StringBuilder buffer
= new StringBuilder((int) response.getEntity().getContentLength());
try {
char[] tmp = new char[1024];
int l;
while ((l = reader.read(tmp)) != 1) {
buffer.append(tmp,
0, l);
}
}
finally {
reader.close();
}
String string
= buffer.toString();
//Log.e(“json”, “rs:” + string);
response.getEntity().consumeContent();
JSONObject data
=new JSONObject(string);
if(data!=null){
JSONObject u
=data.getJSONObject(user);
String userName
=u.getString(screen_name);
String userIcon
=u.getString(profile_image_url);
Log.e(
userIcon, userIcon);
String time
=data.getString(created_at);
String text
=data.getString(text);

TextView utv=(TextView)findViewById(R.id.user_name);
utv.setText(userName);
TextView ttv
=(TextView)findViewById(R.id.text);
ttv.setText(text);

ImageView iv=(ImageView)findViewById(R.id.user_icon);
AsyncImageLoader asyncImageLoader
= new AsyncImageLoader();
Drawable cachedImage
= asyncImageLoader.loadDrawable(userIcon,iv, new ImageCallback(){
@Override
public void imageLoaded(Drawable imageDrawable,ImageView imageView, String imageUrl) {

imageView.setImageDrawable(imageDrawable);
}
});
if (cachedImage == null)
{
iv.setImageResource(R.drawable.usericon);
}
else
{
iv.setImageDrawable(cachedImage);
}
if(data.has(bmiddle_pic)){
String picurl
=data.getString(bmiddle_pic);
String picurl2
=data.getString(original_pic);

ImageView pic=(ImageView)findViewById(R.id.pic);
pic.setTag(picurl2);
pic.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
Object obj
=v.getTag();
Intent intent
= new Intent(ViewActivity.this,ImageActivity.class);
Bundle b
=new Bundle();
b.putString(
url, obj.toString());
intent.putExtras(b);
startActivity(intent);
}
});
Drawable cachedImage2
= asyncImageLoader.loadDrawable(picurl,pic, new ImageCallback(){
@Override
public void imageLoaded(Drawable imageDrawable,ImageView imageView, String imageUrl) {
showImg(imageView,imageDrawable);
}
});
if (cachedImage2 == null)
{
//pic.setImageResource(R.drawable.usericon);
}
else
{
showImg(pic,cachedImage2);
}
}
}
}
catch (IllegalStateException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
catch (JSONException e) {
e.printStackTrace();
}
}
url
= http://api.t.sina.com.cn/statuses/counts.json;
params
=new ArrayList();
params.add(
new BasicNameValuePair(source, auth.consumerKey));
params.add(
new BasicNameValuePair(ids, id));
response
=auth.SignRequest(user.getToken(), user.getTokenSecret(), url, params);
if (200 == response.getStatusLine().getStatusCode()){
try {
InputStream is
= response.getEntity().getContent();
Reader reader
= new BufferedReader(new InputStreamReader(is), 4000);
StringBuilder buffer
= new StringBuilder((int) response.getEntity().getContentLength());
try {
char[] tmp = new char[1024];
int l;
while ((l = reader.read(tmp)) != 1) {
buffer.append(tmp,
0, l);
}
}
finally {
reader.close();
}
String string
= buffer.toString();
response.getEntity().consumeContent();
JSONArray data
=new JSONArray(string);
if(data!=null){
if(data.length()>0){
JSONObject d
=data.getJSONObject(0);
String comments
=d.getString(comments);
String rt
=d.getString(rt);
Button btn_gz
=(Button)findViewById(R.id.btn_gz);
btn_gz.setText(
转发(+rt+));
Button btn_pl
=(Button)findViewById(R.id.btn_pl);
btn_pl.setText(
评论(+comments+));
}
}
}
catch (IllegalStateException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
catch (JSONException e) {
e.printStackTrace();
}
}

}

在上面的方法中对于微博中包含的图片显示尺寸进行了特别的处理,如果直接把获取的图片显示在ImageView中,因为当图片宽高超过手机屏幕的时候,系 统会自动按照手机的屏幕按比例缩放图片进行显示,但是我发现一个现象图片的高虽然是按照比例缩小了,但是图片占据的高仍旧是原来图片的高度照成真实图片和 文字内容之间多了很高的一块空白,这个现象非常的奇怪,所以我写了如下方法进行处理:

private void showImg(ImageView view,Drawable img){
int w=img.getIntrinsicWidth();
int h=img.getIntrinsicHeight();
Log.e(
w, w+/+h);
if(w>300)
{
int hh=300*h/w;
Log.e(
hh, hh+“”);
LayoutParams para
=view.getLayoutParams();
para.width
=300;
para.height
=hh;
view.setLayoutParams(para);
}
view.setImageDrawable(img);

}

本篇到这里就结束了,请继续关注下一篇。

[转载]ASP.NET MVC 3.0学习系列文章—NuGet and ASP.NET MVC 3.0

mikel阅读(1125)

[转载]ASP.NET MVC 3.0学习系列文章—NuGet and ASP.NET MVC 3.0 – 爱因斯坦的小脑 – 博客园.

这两周忙着换工作,也没时间更新这个系列的文章。。。。。。。 Smile 在在博客园搜了NuGet这个关键字,结果只有两页,还有几篇文章是我的,所以我觉得还是介绍下NuGet,以及NuGet.org,好让一些初学者能够快速的入门。

ASP.NET MVC 3.0学习系列文章—序

ASP.NET MVC 3.0学习系列文章–Razor and ASP.NET MVC 3.0

ASP.NET MVC 3.0学习系列文章—Controllers in ASP.NET MVC 3.0

ASP.NET MVC 3.0学习系列文章—Model in ASP.NET MVC 3.0

ASP.NET MVC 3.0学习系列文章–Dependency Resolution in ASP.NET MVC 3.0

NuGet-Stickers-550x365

1.Introduction

NuGet is a Visual Studio 2010 extension that makes it easy to add, remove, and update libraries and tools in Visual Studio projects that use the .NET Framework. This topic lists documentation that will help you use NuGet packages and create your own.

NuGet是一个开源的项目,项目的发起人是微软的几个人员。如果你关注NuGet建议关注这两个人:

a.David Ebbo http://blog.davidebbo.com/ Twitter: @davidebbo

b. Phil Haack http://haacked.com/

2.Why NuGet

NuGet的使用可以缩短我们下载和添加dll的时间。不适用nuget的时候,我们添加一个dll需要先下载,解压,添加引用等等。而使用nuGet只需要一步操作。

image

可以看出来,使用NuGet后,dll的配置可以自动添加,而且当有新的版本出现时可以自动更新dll。

使用NuGet另外一个优点是,你添加一个dll后,它会自动把相关联的dll给添加到引用中来。

image

3.安装NuGet

你可以去codeplex上下载NuGet,或者使用VS2010的Extension Manager来安装NuGet:

image

关于NuGet的使用,我这里就给大家说明下如何使用帮助命令:

image

4.NuGet的打包和Feed:

1. 如何打包一个NuGet包: http://nuget.codeplex.com/wikipage?title=Creating%20a%20Package

2.Hosting Your Own Local and Remote NuGet Feeds

http://haacked.com/archive/2010/10/21/hosting-your-own-local-and-remote-nupack-feeds.aspx

5.NuGet.org

image

你只需要注册一个账号就可以上传Package了。 如果你需要和别人分享一个模块或者项目,可以试试使用nuget来分享。。

Nick