2006年04月16日

  一、SWT简介

  Java语言的声望和它在桌面应用程序(GUI程序)所取得的成就显然极不相符,至今仍然很少能看到非常成功Java桌面程序。虽然有JBuilder,Netbean,JProbe等大型软件作为代表,但这仍不能证明Java的GUI程序是成功的:它们的外观总是和同一操作系统平台下的其它软件显得格格不入。对机器配置的需求也似乎永无止境,这使得它们只能被一些总是拥有当前最高性能PC的程序员们所容忍,或是那些不在乎金钱和时间的专业用户所接受。对绝大多数计算机使用者来说,AWT或SWING代表着怪异的界面和无法接受的速度。Standard Widget Toolkit(SWT)或许是Java这一噩梦的终结者,广大Java程序员终于可以开发出高效率的GUI程序,它们拥有标准的外观,几乎没有人能看出你的程序是用Java写出来的,更为重要的是,这些程序是跨平台的。

  SWT本身仅仅是Eclipse组织为了开发Eclipse IDE环境所编写的一组底层图形界面 API。或许是无心插柳,或是有意为之,至今为止,SWT无论是在性能和外观上,都超越了SUN公司提供的AWT和SWING。目前Eclipse IDE已经开发到了2.1版本,SWT已经十分稳定。这里指的稳定应该包含两层意思:

  一是指性能上的稳定,其中的关键是源于SWT的设计理念。SWT最大化了操作系统的图形构件API,就是说只要操作系统提供了相应图形的构件,那么SWT只是简单应用JNI技术调用它们,只有那些操作系统中不提供的构件,SWT才自己去做一个模拟的实现。可以看出SWT的性能上的稳定大多时候取决于相应操作系统图形构件的稳定性。

  另一个稳定是指SWT API包中的类、方法的名称和结构已经少有改变,程序员不用担心由于Eclipse组织开发进度很快(Eclipse IDE每天都会有一个Nightly版本的发布),而导致自己的程序代码变化过大。从一个版本的SWT更新至另一版本,通常只需要简单将SWT包换掉就可以了。

  二、Eclipse3.0的SWT编程

  1.SWT比AWT和Swing要快多,因为它是利用操作系统的界面组件生成UI的,在java桌面设计领域掀起一场革命

  2.环境配置:

  windows系统+eclipse3.0

  3.具体操作:

  (1).新建一java项目,命名SWT,文件结构如下:

  +swt
  +bin(编译输出)
  +src(原文件)
  +AddressBookUI.java
  +swt-awt-win32-3062.dll(以下均从eclipse\plugins\org.eclipse.swt.win32_3.0.1\os\win32\x86下导入)
  +swt-win32-3062.dll
  +javaw.exe.manifest

  (2).到项目的properties里,在java build path | libraries里将swt.jar导入

  (3).AddressBookUI.java原代码如下:


import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
public class AddressBookUI {
 private Shell shell;
 private Text miscText;
 private Text addrText;
 private Text emailText;
 private Text phoneText;
 private Text lnameText;
 private Text fnameText;
 private Button cancelButton;
 private Button saveButton;
 private Button nextButton;
 private Button prevButton;
 private Button editButton;
 private Button deleteButton;
 private Button newButton;
 public static void main(String[] args) {
  AddressBookUI window = new AddressBookUI();
  window.open();
 }
 public void open() {
  final Display display = new Display();
  shell = new Shell();
  shell.setSize(610, 477);
  shell.setText("Address Book");
 {
  newButton = new Button(shell, SWT.NONE);
  newButton.addSelectionListener(new SelectionAdapter() {
   public void widgetSelected(SelectionEvent e) {
    clearText();
    setTextEditable(true);
    enableEditButtons(false);
    enableSaveButtons(true);

    System.out.println("New button selected.");
   }
  });
  newButton.setBounds(10, 380, 75, 35);
  newButton.setText("New");
 }
 {
  deleteButton = new Button(shell, SWT.NONE);
  deleteButton.addSelectionListener(new SelectionAdapter() {
   public void widgetSelected(SelectionEvent e) {
    clearText();

    System.out.println("Delete button selected.");
   }
  });
  deleteButton.setBounds(85, 380, 75, 35);
  deleteButton.setText("Delete");
 }
 {
  editButton = new Button(shell, SWT.NONE);
  editButton.addSelectionListener(new SelectionAdapter() {
   public void widgetSelected(SelectionEvent e) {
    setTextEditable(true);
    enableEditButtons(false);
    enableSaveButtons(true);
  
    System.out.println("Edit button selected.");
   }
  });
  editButton.setBounds(160, 380, 75, 35);
  editButton.setText("Edit");
 }
 {
  prevButton = new Button(shell, SWT.NONE);
  prevButton.addSelectionListener(new SelectionAdapter() {
   public void widgetSelected(SelectionEvent e) {
    System.out.println("Previous button selected.");
   }
  });
  prevButton.setBounds(265, 380, 75, 35);
  prevButton.setText("Previous");
 }
 {
  nextButton = new Button(shell, SWT.NONE);
  nextButton.addSelectionListener(new SelectionAdapter() {
   public void widgetSelected(SelectionEvent e) {
    System.out.println("Next button selected.");
   }
  });
  nextButton.setBounds(340, 380, 75, 35);
  nextButton.setText("Next");
 }
 {
  saveButton = new Button(shell, SWT.NONE);
  saveButton.addSelectionListener(new SelectionAdapter() {
   public void widgetSelected(SelectionEvent e) {
    setTextEditable(false);
    enableEditButtons(true);
    enableSaveButtons(false);
 
    System.out.println("Save button selected.");
   }
  });
  saveButton.setBounds(445, 380, 75, 35);
  saveButton.setText("Save");
  saveButton.setEnabled(false);
 }
 {
  cancelButton = new Button(shell, SWT.NONE);
  cancelButton.addSelectionListener(new SelectionAdapter() {
   public void widgetSelected(SelectionEvent e) {
    setTextEditable(false);
    enableEditButtons(true);
    enableSaveButtons(false);

    System.out.println("Cancel button selected.");
   }
  });
  cancelButton.setBounds(520, 380, 75, 35);
  cancelButton.setText("Cancel");
  cancelButton.setEnabled(false);
 }
 {
  final Group group = new Group(shell, SWT.NONE);
  group.setText("Details");
  group.setBounds(10, 10, 585, 355);
  {
   final Label label = new Label(group, SWT.NONE);
   label.setBounds(10, 20, 135, 25);
   label.setText("First Name:");
  }
  {
   final Label label = new Label(group, SWT.NONE);
   label.setBounds(10, 60, 135, 25);
   label.setText("Last Name:");
  }
  {
   final Label label = new Label(group, SWT.NONE);
   label.setBounds(10, 100, 135, 25);
   label.setText("Phone:");
  }
  {
   final Label label = new Label(group, SWT.NONE);
   label.setBounds(10, 140, 135, 25);
   label.setText("Email:");
  }
  {
   final Label label = new Label(group, SWT.NONE);
   label.setBounds(10, 180, 135, 25);
   label.setText("Address:");
  }
  {
   final Label label = new Label(group, SWT.NONE);
   label.setBounds(10, 255, 135, 25);
   label.setText("Miscellaneous Information:");
  }
  {
   fnameText = new Text(group, SWT.BORDER | SWT.READ_ONLY);
   fnameText.setBounds(150, 15, 420, 25);
  }
  {
   lnameText = new Text(group, SWT.BORDER | SWT.READ_ONLY);
   lnameText.setBounds(150, 55, 420, 25);
  }
  {
   phoneText = new Text(group, SWT.BORDER | SWT.READ_ONLY);
   phoneText.setBounds(150, 95, 420, 25);
  }
  {
   emailText = new Text(group, SWT.BORDER | SWT.READ_ONLY);
   emailText.setBounds(150, 135, 420, 25);
  }
  {
   addrText = new Text(group, SWT.BORDER | SWT.READ_ONLY | SWT.V_SCROLL);
   addrText.setBounds(150, 175, 420, 60);
  }
  {
   miscText = new Text(group, SWT.BORDER | SWT.READ_ONLY | SWT.V_SCROLL);
   miscText.setBounds(150, 250, 420, 65);
  }
 }

 setupMenu();

 shell.open();
 while (!shell.isDisposed()) {
  if (!display.readAndDispatch())
   display.sleep();
 }
}

private void setupMenu() {
 //create the menu bar
 Menu menu = new Menu(shell, SWT.BAR);
 shell.setMenuBar(menu);
 //add the File option to it
 MenuItem file = new MenuItem(menu, SWT.CASCADE);
 file.setText("File");
 //create a menu for the File option
 file.setMenu(new Menu(file));
 //add MenuItems to the File menu
 MenuItem prevItem = new MenuItem(new Menu(file), SWT.NONE);
 prevItem.setText("Previous");

 MenuItem nextItem = new MenuItem(new Menu(file), SWT.PUSH);
 nextItem.setText("Next");

 MenuItem seperator = new MenuItem(new Menu(file), SWT.SEPARATOR);
 MenuItem quitItem = new MenuItem(new Menu(file), SWT.PUSH);
 quitItem.setText("Quit");
 //add listeners for the actions
 prevItem.addListener(SWT.Selection, new Listener() {
  public void handleEvent(Event e) {
   System.out.println("Previous menu item selected.");
  }
 });
 nextItem.addListener(SWT.Selection, new Listener() {
  public void handleEvent(Event e) {
   System.out.println("Next menu item selected.");
  }
 });
 quitItem.addListener(SWT.Selection, new Listener() {
  public void handleEvent(Event e) {
   shell.dispose();
  }
 });
}

private void clearText() {
 fnameText.setText("");
 lnameText.setText("");
 phoneText.setText("");
 emailText.setText("");
 addrText.setText("");
 miscText.setText("");
}

private void setTextEditable(boolean editable) {
 fnameText.setEditable(editable);
 lnameText.setEditable(editable);
 phoneText.setEditable(editable);
 emailText.setEditable(editable);
 addrText.setEditable(editable);
 miscText.setEditable(editable);
}

private void enableEditButtons(boolean enable) {
 newButton.setEnabled(enable);
 deleteButton.setEnabled(enable);
 editButton.setEnabled(enable);
}

private void enableSaveButtons(boolean enable) {
 saveButton.setEnabled(enable);
 cancelButton.setEnabled(enable);
}
}


  (4).测试运行,run就可以啦

出租司机给我上的MBA课
微软中国公司全球技术支持部经理 刘润

我要从徐家汇赶去机场,于是匆匆结束了一个会议,在美罗大厦前搜索出租车。一辆大众发现了我,非常专业的、径直的停在我的面前。这一停,于是有了后面的这个让我深感震撼的故事,象上了一堂生动的MBA案例课。为了忠实于这名出租车司机的原意,我凭记忆尽量重复他原来的话。

“去哪里……好的,机场。我在徐家汇就喜欢做美罗大厦的生意。这里我只做两个地方。美罗大厦,均瑶大厦。你知道吗?接到你之前,我在美罗大厦门口兜了两圈,终于被我看到你了!从写字楼里出来的,肯定去的不近~~~”

“哦?你很有方法嘛!”我附和了一下。

“做出租车司机,也要用科学的方法。”他说。我一愣,顿时很有些兴趣“什么科学的方法?”

“要懂得统计。我做过精确的计算。我说给你听啊。我每天开17个小时的车,每小时成本34.5元……”

“怎么算出来的?”我追问。

“你算啊,我每天要交380元,油费大概210元左右。一天17小时,平均每小时固定成本22元,交给公司,平均每小时12.5元油费。这是不是就是34.5 元?”,我有些惊讶。我打了10年的车,第一次听到有出租车司机这么计算成本。以前的司机都和我说,每公里成本0.3元,另外每天交多少钱之类的。

“成本是不能按公里算的,只能按时间算。你看,计价器有一个“检查”功能。你可以看到一天的详细记录。我做过数据分析,每次载客之间的空驶时间平均为7分钟。如果上来一个起步价,10元,大概要开10分钟。也就是每一个10元的客人要花17分钟的成本,就是9.8元。不赚钱啊!如果说做浦东、杭州、青浦的客人是吃饭,做10元的客人连吃菜都算不上,只能算是撒了些味精。”

强!这位师傅听上去真不象出租车司机,到象是一位成本核算师。“那你怎么办呢?”我更感兴趣了,继续问。看来去机场的路上还能学到新东西。

“千万不能被客户拉了满街跑。而是通过选择停车的地点,时间,和客户,主动地决定你要去的地方。”我非常惊讶,这听上去很有意思。“有人说做出租车司机是靠运气吃饭的职业。我以为不是。你要站在客户的位置上,从客户的角度去思考。”这句话听上去很专业,有点象很多商业管理培训老师说的“put yourself into others’ shoes.”

“给你举个例子,医院门口,一个拿着药的,一个拿着脸盆的,你带哪一个。”我想了想,说不知道。

“你要带那个拿脸盆的。一般人病小痛的到医院看一看,拿点药,不一定会去很远的医院。拿着脸盆打车的,那是出院的。住院哪有不死人的?今天二楼的谁死了,明天三楼又死了一个。从医院出来的人通常会有一种重获新生的感觉,重新认识生命的意义,健康才最重要。那天这个说:走,去青浦。眼睛都不眨一下。你说他会打车到人民广场,再去做青浦线吗?绝对不会!”

我不由得开始佩服。

“再给你举个例子。那天人民广场,三个人在前面招手。一个年轻女子,拿着小包,刚买完东西。还有一对青年男女,一看就是逛街的。第三个是个里面穿绒衬衫的,外面羽绒服的男子,拿着笔记本包。我看一个人只要3秒钟。我毫不犹豫地停在这个男子面前。这个男的上车后说:延安高架、南北高架~~~还没说后面就忍不住问,为什么你毫不犹豫地开到我面前?前面还有两个人,他们要是想上车,我也不好意思和他们抢。我回答说,中午的时候,还有十几分钟就1点了。那个女孩子是中午溜出来买东西的,估计公司很近;那对男女是游客,没拿什么东西,不会去很远;你是出去办事的,拿着笔记本包,一看就是公务。而且这个时候出去,估计应该不会近。那个男的就说,你说对了,去宝山。”

“那些在超市门口,地铁口打车,穿着睡衣的人可能去很远吗?可能去机场吗?机场也不会让她进啊。”

有道理!我越听越有意思。

“很多司机都抱怨,生意不好做啊,油价又涨了啊,都从别人身上找原因。我说,你永远从别人身上找原因,你永远不能提高。从自己身上找找看,问题出在哪里。”这话听起来好熟,好像是“如果你不能改变世界,就改变你自己”,或者Steven Corvey的“影响圈和关注圈”的翻版。“有一次,在南丹路一个人拦车,去田林。后来又有一次,一个人在南丹路拦车,还是去田林。我就问了,怎么你们从南丹路出来的人,很多都是去田林呢?人家说,在南丹路有一个公共汽车总站,我们都是坐公共汽车从浦东到这里,然后搭车去田林的。我恍然大悟。比如你看我们开过的这条路,没有写字楼,没有酒店,什么都没有,只有公共汽车站,站在这里拦车的多半都是刚下公共汽车的,再选择一条最短路经打车。在这里拦车的客户通常不会高于15元。”

“所以我说,态度决定一切!”我听十几个总裁讲过这句话,第一次听出租车司机这么说。

“要用科学的方法,统计学来做生意。天天等在地铁站口排队,怎么能赚到钱?每个月就赚500块钱怎么养活老婆孩子?这就是在谋杀啊!慢性谋杀你的全家。要用知识武装自己。学习知识可以把一个人变成聪明的人,一个聪明的人学习知识可以变成很聪明的人。一个很聪明的人学习知识,可以变成天才。”

“有一次一个人打车去火车站,问怎么走。他说这么这么走。我说慢,上高架,再这么这么走。他说,这就绕远了。我说,没关系,你经常走你有经验,你那么走50块,你按我的走法,等里程表50块了,我就翻表。你只给50快就好了,多的算我的。按你说的那么走要50分钟,我带你这么走只要25分钟。最后,按我的路走,多走了4公里,快了25分钟,我只收了50块。乘客很高兴,省了10元钱左右。这4公里对我来说就是1块多钱的油钱。我相当于用1元多钱买了25分钟。我刚才说了,我一小时的成本34.5块,我多合算啊!”

“在大众公司,一般一个司机3、4千,拿回家。做的好的大概5千左右。顶级的司机大概每月能有7000。全大众2万个司机,大概只有2-3个司机,万里挑一,每月能拿到8000以上。我就是这2-3个人中间的一个。而且很稳定,基本不会大的波动。”

太强了!到此为止,我越来越佩服这个出租车司机。

“我常常说我是一个快乐的车夫。有人说,你是因为赚的钱多,所以当然快乐。我对他们说,你们正好错了。是因为我有快乐、积极的心态,所以赚的钱多。”

说的多好啊!

“要懂得体味工作带给你的美。堵在人民广场的时候,很多司机抱怨,又堵车了!真是倒霉。千万不要这样,用心体会一下这个城市的美,外面有很多漂亮的女孩子经过,非常现代的高楼大厦,虽然买不起,但是却可以用欣赏的眼光去享受。开车去机场,看着两边的绿色,冬天是白色的,多美啊。再看看里程表,100多了,就更美了!每一样工作都有她美丽的地方,我们要懂得从工作中体会这种美丽。”

“我10年前是强生公司的总教练。8年前在公司作过三个不同部门的部门经理。后来我不干了,一个月就3、5千块,没意思。就主动来做司机。我愿意做一个快乐的车夫。哈哈哈哈。”

到了机场,我给他留了一张名片,说:“你有没有兴趣这个星期五,到我办公室,给微软的员工讲一讲你怎么开出租车的?你就当打着表,60公里一小时,你讲多久,我就付你多少钱。给我电话。”

我迫不及待的在飞机上记录下他这堂生动的MBA课。

2006年04月12日

Base64

    Base64和下面将要介绍的Quoted-Printable都属于MIME(多部分( multi-part)、多媒体电子邮件和 WWW 超文本的一种编码标准,用于传送诸如图形、声音和传真等非文本数据)。MIME定义在RFC1341中。

    Base64是现今在互联网上应用最多的一种编码,几乎所有的电子邮件软件头把它作为默认的二进制编码,它已经成了现今电子邮件编码的代名词。

    下面是Base64的一个例子,从例子中,您也可以看到Base64与电子邮件的的紧密联系:

Content-Type: text/plain;charset="cn-gb"

Content-Transfer-Encoding: BASE64

 

CQkJICAgIKG2wtLC68vjt6i088irobcNCgnX99XfOm1vZ2Fvo6yw19TGu8a619W+o6h0ZWxuZXQ6

Ly8yMDIuMTEyLjIwLjEzMjoyM6Ops8nUsaGjDQoJICAgICAgxKq438jtvP65pNf3ytKjumh0dHA6

Ly9tb2dhby5iZW50aXVuLm5ldA0KCQkJRW1haWx0bzptb2dhb0AzNzEubmV0DQoJICAgKioqKioq

KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqICAgICAgICAgICAgICAgDQoJ

ICAgKiCz/cHLvMfS5Mqyw7S2vLK7tPjX36Oss/3By9fjvKPKssO0tryyu8H0z8IqDQoJICAgKioq

KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq

 

    你可以把它单独存成一个文件,可以取名为:mogao.eml,双击可以用OutLook打开(前两行为邮件的原始信息,从第四行开始为编码内容)。

    Base64的算法同Uuencode的算法很接近,也很简单:它将字符流顺序放入一个 24 位的缓冲区,缺字符的地方补零。然后将缓冲区截断成为 4 个部分,高位在先,每个部分 6 位,用下面的64个字符重新表示:“ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/”。如果输入只有一个或两个字节,那么输出将用等号“=”补足。这可以隔断附加的信息造成编码的混乱。它每行一般为76个字符。

    下面我给出Base64的编码和解码的C语言描述:

/*Base64编码*/

void Base64(unsigned char chasc[3],unsigned char chuue[4])

/* 

  chasc:未编码的二进制代码

  chuue:编码过的Base64代码

*/

{

 int i,k=2;

 unsinged char t=NULL;

 for(i=0;i<3;i++)

 {

  *(chuue+i)=*(chasc+i)>>k;

  *(chuue+i)|=t;

  t=*(chasc+i)<<(8-k);

  t>>=2;

  k+=2;

 }

 *(chuue+3)=*(chasc+2)&63;

 

 for(i=0;i<4;i++)

    if((*(chuue+i)>=0)&&(*(chuue+i)<=25)) *(chuue+i)+=65;

    else if((*(chuue+i)>=26)&&(*(chuue+i)<=51)) *(chuue+i)+=71;

    else if((*(chuue+i)>=52)&&(*(chuue+i)<=61)) *(chuue+i)-=4;

    else if(*(chuue+i)==62) *(chuue+i)=43;

    else if(*(chuue+i)==63) *(chuue+i)=47;

 

}

/*Base64解码*/

void unBase64(unsigned char chuue[4],unsigned char chasc[3])

/* 

chuue:未解码的Base64代码

chasc:解码过的二进制代码

*/

{int i,k=2;

 unsigned char t=NULL;

 

 for(i=0;i<4;i++)

     if((*(chuue+i)>=65)&&(*(chuue+i)<=90)) *(chuue+i)-=65;

     else if((*(chuue+i)>=97)&&(*(chuue+i)<=122)) *(chuue+i)-=71;

     else if((*(chuue+i)>=48)&&(*(chuue+i)<=57)) *(chuue+i)+=4;

     else if(*(chuue+i)==43) *(chuue+i)=62;

     else if(*(chuue+i)==47) *(chuue+i)=63;

     else if(*(chuue+i)==61) *(chuue+i)=0;

 

 for(i=0;i<3;i++)

 {*(chhex+i)=*(chuue+i)<<k;

  k+=2;

  t=*(chuue+i+1)>>8-k;

  *(chhex+i)|=t;

 }

}

以下是VB代码:

Base64的程序,入口函数是Encode(bytFile() as Byte),bytFile是一个Byte型的数组,返回一个字符串。对了,传入的数组不大于32767个元素
———————————————————-
Private m_bytIndex(0 To 63) As Byte


‘****************************************************
‘*                                                  *
‘*– To encode file data with Base64 method.        *
‘*                                                  *
‘****************************************************
Public Function Encode(bytFile() As Byte) As String
    
    Dim i As Long, j As Long
    Dim strRslt As String
    
    i = 0
    
    For i = 0 To UBound(bytFile) – ((UBound(bytFile) + 1) Mod 3) Step 3
        strRslt = strRslt + Chr(m_bytIndex(Int((bytFile(i) And 252) / 4)))
        strRslt = strRslt + Chr(m_bytIndex(Int((bytFile(i) And 3) * 16 + (bytFile(i + 1) And 240) / 16)))
        strRslt = strRslt + Chr(m_bytIndex(Int((bytFile(i + 1) And 15) * 4 + (bytFile(i + 2) And 192) / 64)))
        strRslt = strRslt + Chr(m_bytIndex(Int(bytFile(i + 2) And 63)))
    Next i
    
    Select Case ((UBound(bytFile) + 1) Mod 3)
        Case 1
            strRslt = strRslt + Chr(m_bytIndex(Int((bytFile(UBound(bytFile)) And 252) / 4)))
            strRslt = strRslt + Chr(m_bytIndex(Int((bytFile(UBound(bytFile)) And 3) * 16)))
            strRslt = strRslt + "=="
        Case 2
            strRslt = strRslt + Chr(m_bytIndex(Int((bytFile(UBound(bytFile) – 1) And 252) / 4)))
            strRslt = strRslt + Chr(m_bytIndex(Int((bytFile(UBound(bytFile) – 1) And 3) * 16 + (bytFile(UBound(bytFile)) And 240) / 16)))
            strRslt = strRslt + Chr(m_bytIndex(Int((bytFile(UBound(bytFile)) And 15) * 4)))
            strRslt = strRslt + "="
    End Select
    Encode = strRslt
    
End Function


‘****************************************************
‘*                                                  *
‘*– Class Initialize to initialize the array of    *
‘*   base64 coding.                                 *
‘*                                                  *
‘****************************************************
Private Sub Class_Initialize()
    
    m_bytIndex(0) = 65 ‘Asc("A")
    m_bytIndex(1) = 66 ‘Asc("B")
    m_bytIndex(2) = 67 ‘Asc("C")
    m_bytIndex(3) = 68 ‘Asc("D")
    m_bytIndex(4) = 69 ‘Asc("E")
    m_bytIndex(5) = 70 ‘Asc("F")
    m_bytIndex(6) = 71 ‘Asc("G")
    m_bytIndex(7) = 72 ‘Asc("H")
    m_bytIndex(8) = 73 ‘Asc("I")
    m_bytIndex(9) = 74 ‘Asc("J")
    m_bytIndex(10) = 75 ‘Asc("K")
    m_bytIndex(11) = 76 ‘Asc("L")
    m_bytIndex(12) = 77 ‘Asc("M")
    m_bytIndex(13) = 78 ‘Asc("N")
    m_bytIndex(14) = 79 ‘Asc("O")
    m_bytIndex(15) = 80 ‘Asc("P")
    m_bytIndex(16) = 81 ‘Asc("Q")
    m_bytIndex(17) = 82 ‘Asc("R")
    m_bytIndex(18) = 83 ‘Asc("S")
    m_bytIndex(19) = 84 ‘Asc("T")
    m_bytIndex(20) = 85 ‘Asc("U")
    m_bytIndex(21) = 86 ‘Asc("V")
    m_bytIndex(22) = 87 ‘Asc("W")
    m_bytIndex(23) = 88 ‘Asc("X")
    m_bytIndex(24) = 89 ‘Asc("Y")
    m_bytIndex(25) = 90 ‘Asc("Z")
    m_bytIndex(26) = 97 ‘Asc("a")
    m_bytIndex(27) = 98 ‘Asc("b")
    m_bytIndex(28) = 99 ‘Asc("c")
    m_bytIndex(29) = 100 ‘Asc("d")
    m_bytIndex(30) = 101 ‘Asc("e")
    m_bytIndex(31) = 102 ‘Asc("f")
    m_bytIndex(32) = 103 ‘Asc("g")
    m_bytIndex(33) = 104 ‘Asc("h")
    m_bytIndex(34) = 105 ‘Asc("i")
    m_bytIndex(35) = 106 ‘Asc("j")
    m_bytIndex(36) = 107 ‘Asc("k")
    m_bytIndex(37) = 108 ‘Asc("l")
    m_bytIndex(38) = 109 ‘Asc("m")
    m_bytIndex(39) = 110 ‘Asc("n")
    m_bytIndex(40) = 111 ‘Asc("o")
    m_bytIndex(41) = 112 ‘Asc("p")
    m_bytIndex(42) = 113 ‘Asc("q")
    m_bytIndex(43) = 114 ‘Asc("r")
    m_bytIndex(44) = 115 ‘Asc("s")
    m_bytIndex(45) = 116 ‘Asc("t")
    m_bytIndex(46) = 117 ‘Asc("u")
    m_bytIndex(47) = 118 ‘Asc("v")
    m_bytIndex(48) = 119 ‘Asc("w")
    m_bytIndex(49) = 120 ‘Asc("x")
    m_bytIndex(50) = 121 ‘Asc("y")
    m_bytIndex(51) = 122 ‘Asc("z")
    m_bytIndex(52) = 48 ‘Asc("0")
    m_bytIndex(53) = 49 ‘Asc("1")
    m_bytIndex(54) = 50 ‘Asc("2")
    m_bytIndex(55) = 51 ‘Asc("3")
    m_bytIndex(56) = 52 ‘Asc("4")
    m_bytIndex(57) = 53 ‘Asc("5")
    m_bytIndex(58) = 54 ‘Asc("6")
    m_bytIndex(59) = 55 ‘Asc("7")
    m_bytIndex(60) = 56 ‘Asc("8")
    m_bytIndex(61) = 57 ‘Asc("9")
    m_bytIndex(62) = 43 ‘Asc("+")
    m_bytIndex(63) = 47 ‘Asc("/")

End Sub

2006年04月11日

        3G牌照即将发放,“三网融合”被写进“十一五”规划,而移动通信软件工程师将成为2006年人才市场新宠。
  专家表示,电信网、互联网、电视网三大网络未来都将走向宽带化、IP化和移动化,尤其是移动通信网和互联网的结合将催生很多新的业务模式,因此既掌握软件开发技术又精通移动通信技术的新型软件人才将成为市场主流,而传统的软件工程师则不再适应市场需要。计世资讯的研究报告指出,保守估计,3G人才的市场缺口在未来的两三年内就将达到50万人以上。

嵌入式系统是以应用为中心,以计算机技术为基础,并且软硬件可裁剪,适用于应用系统对功能、可靠性、成本、体积、功耗有严格要求的专用计算机系统。它一般由嵌入式微处理器、外围硬件设备、嵌入式操作系统以及用户的应用程序等四个部分组成,用于实现对其他设备的控制、监视或管理等功能。

嵌入式系统一般指非PC系统,它包括硬件和软件两部分。硬件包括处理器/微处理器、存储器及外设器件和I/O端口、图形控制器等。软件部分包括操作系统软件(OS)(要求实时和多任务操作)和应用程序编程。有时设计人员把这两种软件组合在一起。应用程序控制着系统的运作和行为;而操作系统控制着应用程序编程与硬件的交互作用。

嵌入式系统的核心是嵌入式微处理器。嵌入式微处理器一般就具备以下4个特点:

1)对实时多任务有很强的支持能力,能完成多任务并且有较短的中断响应时间,从而使内部的代码和实时内核心的执行时间减少到最低限度。

2)具有功能很强的存储区保护功能。这是由于嵌入式系统的软件结构已模块化,而为了避免在软件模块之间出现错误的交叉作用,需要设计强大的存储区保护功能,同时也有利于软件诊断。

3)可扩展的处理器结构,以能最迅速地开展出满足应用的最高性能的嵌入式微处理器。

4)嵌入式微处理器必须功耗很低,尤其是用于便携式的无线及移动的计算和通信设备中靠电池供电的嵌入式系统更是如此,如需要功耗只有mW甚至μW级。

嵌入式计算机系统同通用型计算机系统相比具有以下特点:

1.嵌入式系统通常是面向特定应用的嵌入式CPU与通用型的最大不同就是嵌入式CPU大多工作在为特定用户群设计的系统中,它通常都具有低功耗、体积小、集成度高等特点,能够把通用CPU中许多由板卡完成的任务集成在芯片内部,从而有利于嵌入式系统设计趋于小型化,移动能力大大增强,跟网络的耦合也越来越紧密。

2.嵌入式系统是将先进的计算机技术、半导体技术和电子技术与各个行业的具体应用相结合后的产物。这一点就决定了它必然是一个技术密集、资金密集、高度分散、不断创新的知识集成系统。

3.嵌入式系统的硬件和软件都必须高效率地设计,量体裁衣、去除冗余,力争在同样的硅片面积上实现更高的性能,这样才能在具体应用中对处理器的选择更具有竞争力。

4.嵌入式系统和具体应用有机地结合在一起,它的升级换代也是和具体产品同步进行,因此嵌入式系统产品一旦进入市场,具有较长的生命周期。

5.为了提高执行速度和系统可靠性,嵌入式系统中的软件一般都固化在存储器芯片或单片机本身中,而不是存贮于磁盘等载体中。

6.嵌入式系统本身不具备自举开发能力,即使设计完成以后用户通常也是不能对其中的程序功能进行修改的,必须有一套开发工具和环境才能进行开发。



嵌入式系统(Embedded System)是当今最热门的领域之一,它的定义为:嵌入式系统是以应用为中心,以计算机技术为基础,并且软硬件可裁剪,适用于应用系统对功能、可靠性、成本、体积、功耗有严格要求的专用计算机系统。嵌入式系统一般由嵌入式微处理器、外围硬件设备、嵌入式操作系统以及用户的应用程序4个部分组成,用于实现对其他设备的控制、监视或管理等功能。


嵌入式系统有以下特点:


1)嵌入式系统低功耗、体积小,专用性强。嵌入式系统与PC机的最大不同就是嵌入式CPU能够把PC机中许多由板卡完成的任务集成在芯片内部,从而有利于嵌入式系统设计趋于小型化。


2)为了提高执行速度和系统可靠性,嵌入式系统中的软件一般都固化在存储器芯片本身中,而不是存贮于磁盘等载体中。


3)嵌入式系统的硬件和软件都必须高效率地设计,系统要精简。操作系统一般和应用软件集成在一起。


4)对软件代码质量要求很高。应该尽最大可能避免死机的情况发生。


5)嵌入式系统开发需要专门的开发工具和开发环境。


嵌入式系统是一个技术密集,资金密集,高度分散,不断创新的基于硅片的知识集成系统。今天的嵌入式系统已普遍应用于国防电子、数字家庭、工业自动化、汽车电子、医学科技、消费电子、无线通讯、电力系统等国民经济的主要行业。随着嵌入式技术的发展,嵌入式系统将更广泛地应用于人类生活的方方面面。


现在嵌入式系统正处于高速发展阶段,未来的几年里,这种发展和竞争将越来越激烈。嵌入式操作系统、捆绑工具及相关服务的市场到2007年将超过10亿美元。非捆绑嵌入式软件开发工具的市场将达到5.252亿美元,年复合增长率为9.5%


2006年04月10日

前面的是师傅的反汇编,这里介绍一种更加简单的方法,在宁波大学这里是可以用地,其他的应该也可以吧~~

其实这个也是师傅提供的方法,发现师傅真强~~

看了图片应该就知道怎么接局域网了吧,其实一切都是那么简单,有了这个思路,其实我们的网通也是不需要买路由器的,为何不利用下楼道上的交换机呢?

我们学校的校园网宽带真是变态,禁止多网卡,禁止多IP,禁止代理软件上网.网费贵不说!晚上还断网.学校采用的是信联易通的AMBER网关产品!学校同学深受其迫害,让电信从我们这些穷学生这里大把大把的捞钱.今天我就将Pubinfo client 客户端用c32asm反汇编出来,经过研究,发现原来禁止多网卡,禁多IP,检测代理软件原来是靠c:\windows\system\下的一个名叫change.dll文件实现的!change.dll复制出来,查壳,居然没加壳,呵呵,直接用记事本打开看看,用记事本查看EXE,DLL等文件是我最喜欢的!结果看到:

请不要使用代理!!    请不要使用代理! SYSTEM\ControlSet001\Services\spserv
SOFTWARE\Qbik Software\WinGate 

SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\HomeShare宽带共享_is1  

SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\SyGate  请卸载代理软件!

SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CCProxy_is1 SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CCProxy

这些就的检测代理软件的条件哦,只要删除注册表相关键值就可以突破代理软件检测了!

那多网卡,IP怎么办啊?也是啊!前段时间有几款301P的摄像头的驱动会安装个虚拟网卡,给我们带来了很多麻烦,也用c32asm打开看看,找到字符串,如图1


 
1
我们马上可以找到如下语句:

::100022BC::  BF 3C610010              MOV EDI,1000613C                \->: 请不要使用代理!!

::10002285::  BF 50610010              MOV EDI,10006150                  \->: 请不要使用代理!

::1000214B::  BF 30620010              MOV EDI,10006230                   \->: 请卸载代理软件!

::10002056::  BF BC620010              MOV EDI,100062BC                  \->: 使用多块网卡!

::10001F7A::  BF DC620010              MOV EDI,100062DC                 \->: 网卡有多个地址!

(省略若干过程代码)

只要中间判断不成立,程序立即终止,会弹出提示,当我们点击确定时,客户端就会发送数据与服务器断开连接.我们就拿多块网卡检测来分析吧! 如图2

                                        
2

当程序检测到一块网卡时返回真值,程序跳到1000207B

跳转代码如下:

::1000204A::  7E 2F                    JLE SHORT 1000207B

我们把JLE该成JMP让程序无条件跳转不就可以了吗!修改后的代码如下:

::1000204A::  EB 2F                    JMP SHORT 1000207B

其他地方的跳转也改成无条件跳转就OK!change.dll保存,用修改的文件覆盖c:\windows\system\下的change.dll文件,这样检测就失效了!呵呵!

测试一下,程序正常运行,限制已经去除,但是在程序首次进行与网络进行验证时,c:\windows\system\下的change.dll文件会被修改的,那是怎么回事呢??我再次打开c32asm反汇编connect.exe发现有change.dll校验,而且有五处,三处是对c:\windows\system\下的change.dll进行访问的,还有两处是什么呢?可能是向服务器请求change.dll文件.如图3

                                           
3

::00404A80::  68 24944000              PUSH 409424                               \->: change.dll

::00404A85::  68 24944000              PUSH 409424                               \->: change.dll

就是这,发现原来给我想的一样,但是程序好象没有一直都向服务器请求change.dll文件的,肯定有地方进行判断的!查看代码:

::00404A12::  0F85 97000000            JNZ 00404AAF                            \:JMPDOWN 当检测不为零时跳转

要是改成无条件跳转就可以不让connect.exe不更新change.dll!

::00404A12::  EB85 97000000            JMP 00404AAF                            \:JMPDOWN

保存程序!

至此,破解工具已经完成!我们可以将破解的connect.exe覆盖原文件.

2006年04月06日

两天,整整两天,每天早上6点半就起来,终于把电信的pubinfo给搞定了。搞完后才发现,自己绕了个大圈,这么简单的东西竟然没有想到,最后还是星辰师傅教的~~

总结下:
1。用反汇编:目前还没有学会,这个也是比较感兴趣的,要在最近几天内好好研究下,学回~~~
2。写一个connect.exe这个对我来说最近几个月不太可能,对网络这块太不熟悉了,根本不知道这个整个工作的流程和原理
3。编写一个欺骗服务器的dll,这个可以作为学习写外挂知识的开始!今后的努力方向
4。homeshare一切都是那么简单~~最终的解决方法,这个用的好简单,不知道什么时候电信要在change.dll上做手脚了,到时候就用反汇编OK~~

哈哈,打道电信!破解到底!!!

过几天把这些东西全发上来!!

2006年04月01日

Java中的网络编程是一个很重要的部分,也是其编程优越性的地方之一。在Java中有一个专门的Java.net类库来管理网络编程的有关方法。
  下面先介绍在Java中怎样用socket进行客户与服务器通信。最后再介绍一个一个最简单的通话程序。

一.怎样用socket进行客户与服务器通信
  在Java中用socket进行客户/服务器之间的通信编程。Socket是两个实体之间进行通信的有效端点。通过socket可以获得源IP地址和源端口、终点IP地址和终点端口。用户可以将多个socket连入同一个端口,以便对于单个端口可以有多个连接。通过socket客户/服务器编程可以创建一个能被许多人使用的分布式程序,并且所有客户均可以用统一的前端进行工作,并与服务器进行通信。
  要想与服务器通信必须具备三个条件:服务器程序、客户程序和连接它们的socket程序。这三个部分缺一不可。但是,客户与服务器之间的通信有很多的方式,其中另一个方法是把客户作为索取者,把服务器作为给予者。下面我们看一看Java的服务器编程。
  在Java中,服务器有3个主要的功能:
  1.在Java.net类库中通过构造一个ServerSocket类的实例使服务器能够检测到指定端口的信息。用ServerSocke中的accept()方法可以使服务器检测到指定端口的活动。另外,服务器还负责检测要求与它连接的客户。
?Socket类的实例代表客户与服务器连接成功。通过编程可以使多个用户通过同一个端口与服务器相连,其中都是Socket 类的实例。
  2.可以分别用Socket类的getInputStream()和getOutStream()方法来发送和捕捉数据。其使用方法如下:

  try{
    ServerSocket myServerSocket=new ServerSocket(100);
    Socket my100Socket=myServerSocket.accept();
  }catch(Exception e){}

  在上面的代码中,首先构造一个ServerSocket类的实例,并传递给它一个整数作为服务器指定可以使用的给定端口,如下:

  ServerSocket myServerSocket=new ServerSocket(100);

  在Java程序中如果每次构造ServerSocket时都能保持捕捉异常事件,则就随时指定了准备使用的端口。下面的代码使用accept()方法来检测端口的活动。

  Socket my100Socket=myServerSocket.accept();

  Accept()方法直到接收到用户的连接请求,才继续执行中断的执行程序。一旦客户的连接成功,my100Socket就代表该连接,并且可以发送和接收数据。
最后,我们看一看客户是怎样请求连接的。其连接方法如下:

  try{
    Socket mySocket=new Socket("www.cpcw.com",100);
  }catch(Exception e ){}

  通过上面的代码可能看出,也是通过Socket类来实现的。下面我们通过一个网络编程的实例来说明如何进行网络通信。

二.一个最简单的通话程序

通话器服务器:
import java.net.*;
import java.io.*;
import java.lang.*;

public class myserver{
public static void main(String args[]){
ServerSocket server;
Socket socket;
String s;
InputStream Is;
OutputStream Os;
DataInputStream DIS;
PrintStream PS;

try{
//在端口4321注册服务
server=new ServerSocket(4321);
socket=server.accept();   //监听窗口,等待连接

System.out.println("server ok");
System.out.println("************************************************");
System.out.println("");

//获得对应Socket的输入/输出流
Is=socket.getInputStream();
Os=socket.getOutputStream();
//建立数据流
DIS=new DataInputStream(Is);
PS=new PrintStream(Os);
DataInputStream in=new DataInputStream(System.in);
while(true){
System.out.println("");
System.out.println("please wait client´s message…");
System.out.println("");
s=DIS.readLine(); //读入从client传来的字符串
System.out.println("client said:"+s); //打印字符串
if(s.trim().equals("BYE"))break; //如果是"BYE",就退出
System.out.print("you say:");
s=in.readLine(); //读取用户输入的字符串
PS.println(s); //将读取得字符串传给client
if(s.trim().equals("BYE"))break; //如果是"BYE",就退出

}

//关闭连接
DIS.close(); //关闭数据输入流
PS.close(); //关闭数据输出流
Is.close(); //关闭输入流
Os.close(); //关闭输出流
socket.close(); //关闭sockey
}
catch(Exception e){
System.out.println("Error:"+e);
}
}
}


通话器客户端
import java.net.*;
import java.io.*;
import java.lang.*;

public class myclient{
public static void main(String args[]){
if (args.length<1){ //判断命令加参数没有
System.out.println("you forget the name of the server!");
System.out.println("see also: myclient yxf");
System.exit(1); //如果没加参数就退出
}

Socket socket;
String s="yxfsoft@263.net";
String len;
InputStream Is;
OutputStream Os;
DataInputStream DIS;
PrintStream PS;
try{
//向主机名为args[0]的服务器申请连接
//注意端口号要与服务器保持一致:4321
socket=new Socket(args[0],4321);

System.out.println("client ok");
System.out.println("************************************************");
System.out.println("");

//获得对应socket的输入/输出流
Is=socket.getInputStream();
Os=socket.getOutputStream();
//建立数据流
DIS=new DataInputStream(Is);
PS=new PrintStream(Os);
DataInputStream in=new DataInputStream(System.in);

while(true){
System.out.print("you say:");
s=in.readLine(); //读取用户输入的字符串
PS.println(s); //将读取得字符串传给server
if(s.trim().equals("BYE"))break; //如果是"BYE",就退出
else
{
System.out.println("");
System.out.println("please wait server´s message…");
System.out.println("");
}
s=DIS.readLine(); //从服务器获得字符串
System.out.println("server said:"+s); //打印字符串
if(s.trim().equals("BYE"))break; //如果是"BYE",就退出

}

//关闭连接
DIS.close(); //关闭数据输入流
PS.close(); //关闭数据输出流
Is.close(); //关闭输入流
Os.close(); //关闭输出流
socket.close(); //关闭socket
}
catch(Exception e){
System.out.println("Error:"+e);
}
}
}

  下载源文件:客户端工程,服务器工程。编程环境为VisualJ++6.0。
  请读者先在一台机器上运行myserver.exe(myserver.exe在服务器工程内),然后在同一台机器或与第一台机器连了网的机器上打开控制台(Dos窗口),然后转到myclient.exe(myclient.exe在客户端工程内)所在的目录,如下运行客户端程序: myclient serverhostName 或 myclient serverhostIp ,serverhostName为运行服务器程序的机器名, serverhostIp为运行服务器程序的机器的IP地址。运行后就可以相互通话了。这个通话程序只能轮换着说话,读者可以在它的基础上丰富其功能。

出于多种目的要写这篇文章,简单的说是因为最近的工作和Java 的Socket相关.所以工作之余就有了写点东西的想法.同样我不希望把文章写的太复杂,因此浅出自己的一点心得.
事实上网络编程简单的理解就是两台计算机相互通讯数据而已.对于程序员而言,去掌握一种编程接口并使用一种编程模型相对就会显得简单的多了.Java SDK提供一些相对简单的Api来完成这些工作.Socket就是其中之一.对于Java而言.这些Api存在与java.net 这个包里面.因此只要导入这个包就可以准备网络编程了.
网络编程的基本模型就是客户机到服务器模型.简单的说就是两个进程之间相互通讯,然后其中一个必须提供一个固定的位置,而另一个则只需要知道这个固定的位置.并去建立两者之间的联系..然后完成数据的通讯就可以了.这里提供固定位置的通常称为服务器,而建立联系的通常叫做客户端.基于这个简单的模型,就可以进入网络编程啦.
Java对这个模型的支持有很多种Api.而这里我只想介绍有关Socket的编程接口.对于Java而言已经简化了Socket的编程接口.首先我们来讨论有关提供固定位置的服务方是如何建立的.Java提供了ServerSocket来对其进行支持.事实上当你创建该类的一个实力对象并提供一个端口资源你就建立了一个固定位置可以让其他计算机来访问你.ServerSocket server=new  ServerSocket(6789);这里稍微要注意的是端口的分配必须是唯一的.因为端口是为了唯一标识每台计算机唯一服务的.另外端口号是从0~65535之间的,前1024个端口已经被Tcp/Ip 作为保留端口,因此你所分配的端口只能是1024个之后的.好了.我们有了固定位置.现在所需要的就是一根连接线了.该连接线由客户方首先提出要求.因此Java同样提供了一个Socket对象来对其进行支持.只要客户方创建一个Socket的实例对象进行支持就可以了.Socket client
=new Socket(InetAddress.getLocalHost(),5678);客户机必须知道有关服务器的IP地址.对于着一点Java也提供了一个相关的类InetAddress 该对象的实例必须通过它的静态方法来提供.它的静态方法主要提供了得到本机IP 和通过名字或IP直接得到InetAddress的方法.
好了.上面的方法基本可以建立一条连线让两台计算机相互交流了.可是数据是如何传输的呢?事实上I/O操作总是和网络编程息息相关的.因为底层的网络是继续数据的.除非远程调用,处理问题的核心在执行上.否则数据的交互还是依赖于I\O操作的.所以你也必须导入java.io这个包.java的IO操作也不复杂.它提供了针对于字节流和Unicode的读者和写者,然后也提供了一个缓冲用于数据的读写.
  BufferedReader in=new BufferedReader(new InputStreamReader(server.getInputStream()));
  PrintWriter out=new PrintWriter(server.getOutputStream());

上面两句就是建立缓冲并把原始的字节流转变为Unicode可以操作.而原始的字节流来源于Socket的两个方法.getInputStream()和getOutputStream()方.分别用来得到输入和输出.那么现在有了基本的模型和基本的操作工具.我们可以做一个简单的Socket例程了.
服务方:
import java.io.*;
import java.net.*;

public class MyServer {
public static void main(String[] args) throws IOException{
  ServerSocket server=new ServerSocket(5678);
  Socket client=server.accept();
BufferedReader in=new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out=new PrintWriter(client.getOutputStream());
while(true){
String str=in.readLine();
System.out.println(str);
out.println("has receive….");
out.flush();
if(str.equals("end"))
break;
}
client.close();
}
}

这个程序的主要目的在于服务器不断接收客户机所写入的信息只到.客户机发送"End"字符串就退出程序.并且服务器也会做出"Receive"为回应.告知客户机已接收到消息.

客户机代码:
import java.net.*;
import java.io.*;

public class Client{
static Socket server;

public static void main(String[] args)throws Exception{
  server=new Socket(InetAddress.getLocalHost(),5678);
  BufferedReader in=new BufferedReader(new InputStreamReader(server.getInputStream()));
  PrintWriter out=new PrintWriter(server.getOutputStream());
  BufferedReader wt=new BufferedReader(new InputStreamReader(System.in));
  
  while(true){
   String str=wt.readLine();
   out.println(str);
   out.flush();
   if(str.equals("end")){
    break;
   }
   System.out.println(in.readLine());
  }
  server.close();
}
}

客户机代码则是接受客户键盘输入,并把该信息输出,然后输出"End"用来做退出标识.

这个程序只是简单的两台计算机之间的通讯.如果是多个客户同时访问一个服务器呢?你可以试着再运行一个客户端,结果是会抛出异常的.那么多个客户端如何实现呢?

其实,简单的分析一下,就可以看出客户和服务通讯的主要通道就是Socket本身.而服务器通过accept方法就是同意和客户建立通讯.这样当客户建立Socket的同时.服务器也会使用这一根连线来先后通讯.那么既然如此只要我们存在多条连线就可以了.那么我们的程序可以变为如下:

服务器:

import java.io.*;
import java.net.*;

public class MyServer {
public static void main(String[] args) throws IOException{
  ServerSocket server=new ServerSocket(5678);
while(true){
  Socket client=server.accept();
BufferedReader in=new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out=new PrintWriter(client.getOutputStream());
while(true){
String str=in.readLine();
System.out.println(str);
out.println("has receive….");
out.flush();
if(str.equals("end"))
break;
}
client.close();
}
}
}

这里仅仅只是加了一个外层的While循环.这个循环的目的就是当一个客户进来就为它分配一个Socket直到这个客户完成一次和服务器的交互,这里也就是接受到客户的"End"消息.那么现在就实现了多客户之间的交互了.但是.问题又来了.这样做虽然解决了多客户,可是是排队执行的.也就是说当一个客户和服务器完成一次通讯之后下一个客户才可以进来和服务器交互.无法做到同时服务.那么要如何才能同时达到既能相互之间交流又能同时交流呢?很显然这是一个并行执行的问题了.所以线程是最好的解决方案.
那么下面的问题是如何使用线程.首先要做的事情是创建线程并使得其可以和网络连线取得联系.然后由线程来执行刚才的操作.要创建线程要么直接继承Thread要么实现Runnable接口,要建立和Socket的联系只要传递引用就可以了.而要执行线程就必须重写run方法.而run方法所做的事情.就是刚才单线程版本main所做的事情.因此我们的程序变成了这样:
import java.net.*;
import java.io.*;

public class MultiUser extends Thread{
private Socket client;

public MultiUser(Socket c){
  this.client=c;
}

public void run(){
  try{   
   BufferedReader in=new BufferedReader(new InputStreamReader(client.getInputStream()));
   PrintWriter out=new PrintWriter(client.getOutputStream());
    //Mutil User but can’t parallel
    while(true){
     String str=in.readLine();
     System.out.println(str);
     out.println("has receive….");
     out.flush();
     if(str.equals("end"))
      break;
    }
   client.close();  
   }catch(IOException ex){
   }finally{
    
   }
}

public static void main(String[] args)throws IOException{
  ServerSocket server=new ServerSocket(5678);
  while(true){
   //transfer location change Single User or Multi User
   MultiUser mu=new MultiUser(server.accept());
   mu.start();
  }
}
}

我的类直接从Thread类继承了下来.并且通过构造函数传递引用和客户Socket建立了联系.这样每个线程就有了.一个通讯管道.同样我们可以填写run方法.把之前的操作交给线程来完成.这样多客户并行的Socket就建立起来了.

我的文章写完了.虽然我的文章到这里就结束了.可是要和Java的Socket相关的操作还有很多.各位大侠继续努力吧….