Java开发网 Java开发网
注册 | 登录 | 帮助 | 搜索 | 排行榜 | 发帖统计  

您没有登录

» Java开发网 » Java SE 综合讨论区  

按打印兼容模式打印这个话题 打印话题    把这个话题寄给朋友 寄给朋友    该主题的所有更新都将Email到你的邮箱 订阅主题
flat modethreaded modego to previous topicgo to next topicgo to back
作者 java 网络编程
cckoan

Smoking

CJSDN高级会员


发贴: 253
积分: 130
于 2002-12-31 10:32 user profilesend a private message to usersearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
在实际的网络运行操作中,我们经常会涉及到Web服务器、Mail服务器和FTP服务器。浏览网页、发送邮件和传送文件等功能的实现主要是通过Web服务器、Mail服务器和FTP服务器来完成的。因此,学习基于HTTP、SMTP和FTP等协议构建的Web、Mail和FTP服务器,对于理解Java的网络编程是很有意义的。下面我们就Java网络编程做些说明和探讨。

HTTP协议

超文本传输协议(HTTP)是位于TCP/IP 协议的应用层,是最广为人知的协议,也是互连网中最核心的协议之一,同样,HTTP 也是基于 C/S 或 B/S 模型实现的。事实上,我们使用的浏览器如Netscape 或IE 是实现HTTP 协议中的客户端,而一些常用的Web 服务器软件如Apache、IIS 和iPlanet Web Server 等是实现HTTP 协议中的服务器端。Web 页由服务端资源定位,传输到浏览器,经过浏览器的解释后,被客户所看到。

Web 的工作基于客户机/服务器计算模型,由Web 浏览器(客户机)和Web服务器(服务器)构成,两者之间采用超文本传送协议(HTTP)进行通信。HTTP协议是Web浏览器和Web服务器之间的应用层协议,是通用的、无状态的、面向对象的协议。

一个完整的HTTP协议会话过程包括四个步骤:

◆ 连接,Web浏览器与Web服务器建立连接,打开一个称为Socket(套接字)的虚拟文件,此文件的建立标志着连接建立成功;

◆ 请求,Web浏览器通过Socket向Web服务器提交请求。HTTP的请求一般是GET或POST命令(POST用于FORM参数的传递);

◆ 应答,Web浏览器提交请求后,通过HTTP协议传送给Web服务器。Web服务器接到后,进行事务处理,处理结果又通过HTTP传回给Web浏览器,从而在Web浏览器上显示出所请求的页面;

◆ 关闭连接,应答结束后Web浏览器与Web服务器必须断开,以保证其它Web浏览器能够与Web服务器建立连接。

Java实现Web服务器功能的程序设计

编程思路

根据上述HTTP协议的会话过程,本实例中实现了GET请求的Web服务器程序的方法,方法如下:

通过创建ServerSocket 类对象,侦听用户指定的端口(为8080),等待并接受客户机请求到端口。创建与Socket相关联的输入流和输出流,然后读取客户机的请求信息。若请求类型是GET,则从请求信息中获取所访问的HTML 文件名;如果HTML 文件存在,则打开HTML 文件,把HTTP 头信息和HTML 文件内容通过Socket 传回给Web浏览器,然后关闭文件,否则发送错误信息给Web 浏览器。最后关闭与相应Web 浏览器连接的Socket。

用Java编写Web服务器httpServer.java文件的源代码如下:

//httpServer.java
import java.net.*;
import java.io.*;
import java.util.*;
import java.lang.*;
public class httpServer{
public static void main(String args[]) {
  int port;
  ServerSocket server_socket;
//读取服务器端口号
  try {
port = Integer.parseInt(args[0]);
  }
  catch (Exception e) {
   port = 8080;
  }
  try {
//监听服务器端口,等待连接请求
   server_socket = new ServerSocket(port);
   System.out.println("httpServer running on port " +
     server_socket.getLocalPort());
   //显示启动信息
   while(true) {
    Socket socket = server_socket.accept();
    System.out.println("New connection accepted " +
         socket.getInetAddress() +
         ":" + socket.getPort());
    //创建分线程
    try {
     httpRequestHandler request =
      new httpRequestHandler(socket);
     Thread thread = new Thread(request);
     //启动线程
     thread.start();
    }
    catch(Exception e) {
     System.out.printlnEnvelope;
    }
   }
  }
  catch (IOException e) {
   System.out.printlnEnvelope;
  }
}
}
class httpRequestHandler implements Runnable
{
final static String CRLF = "\r\n";
Socket socket;
InputStream input;
OutputStream output;
BufferedReader br;
// 构造方法
public httpRequestHandler(Socket socket) throws Exception
{
  this.socket = socket;
  this.input = socket.getInputStream();
  this.output = socket.getOutputStream();
  this.br =
   new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
// 实现Runnable 接口的run()方法
public void run()
{
  try {
   processRequest();
  }
  catch(Exception e) {
   System.out.printlnEnvelope;
  }
}
private void processRequest() throws Exception
{
  while(true) {
//读取并显示Web 浏览器提交的请求信息
   String headerLine = br.readLine();
   System.out.println("The client request is "+headerLine);
   if(headerLine.equals(CRLF) || headerLine.equals("")) break;
   StringTokenizer s = new StringTokenizer(headerLine);
   String temp = s.nextToken();
   if(temp.equals("GET")) {
    String fileName = s.nextToken();
    fileName = "." + fileName ;
    // 打开所请求的文件
    FileInputStream fis = null ;
    boolean fileExists = true ;
    try
     {
      fis = new FileInputStream( fileName ) ;
     }
    catch ( FileNotFoundException e )
     {
      fileExists = false ;
     }
    // 完成回应消息
    String serverLine = "Server: a simple java httpServer";
    String statusLine = null;
    String contentTypeLine = null;
    String entityBody = null;
    String contentLengthLine = "error";
    if ( fileExists )
     {
      statusLine = "HTTP/1.0 200 OK" + CRLF ;
      contentTypeLine = "Content-type: " +
       contentType( fileName ) + CRLF ;
      contentLengthLine = "Content-Length: "
       + (new Integer(fis.available())).toString()
       + CRLF;
     }
    else
     {
      statusLine = "HTTP/1.0 404 Not Found" + CRLF ;
      contentTypeLine = "text/html" ;
      entityBody = "<HTML>" +
       "<HEAD><TITLE>404 Not Found</TITLE></HEAD>" +
       "<BODY>404 Not Found"
       +"<br>usage:http://yourHostName:port/"
       +"fileName.html</BODY></HTML>" ;
     }
    // 发送到服务器信息
    output.write(statusLine.getBytes());
    output.write(serverLine.getBytes());
    output.write(contentTypeLine.getBytes());
    output.write(contentLengthLine.getBytes());
  output.write(CRLF.getBytes());
    // 发送信息内容
    if (fileExists)
     {
      sendBytes(fis, output) ;
      fis.close();
     }
    else
     {
      output.write(entityBody.getBytes());
     }
   }
  }
//关闭套接字和流
  try {
   output.close();
   br.close();
   socket.close();
  }
  catch(Exception e) {}
}
private static void sendBytes(FileInputStream fis, OutputStream os)
  throws Exception
{
  // 创建一个 1K buffer
  byte[] buffer = new byte[1024] ;
  int bytes = 0 ;
  // 将文件输出到套接字输出流中
  while ((bytes = fis.read(buffer)) != -1 )
   {
    os.write(buffer, 0, bytes);
   }
}
private static String contentType(String fileName)
{
  if (fileName.endsWith(".htm") || fileName.endsWith(".html"))
   {
    return "text/html";
   }

  return "fileName";
}
}


编程技巧说明

◆ 主线程设计

主线程的设计就是在主线程httpServer 类中实现了服务器端口的侦听,服务器接受一个客户端请求之后创建一个线程实例处理请求,代码如下:

import java.net.*;
import java.io.*;
import java.util.*;
import java.lang.*;
public class httpServer{
public static void main(String args[]) {
port;
ServerSocket server_socket;
//读取服务器端口号
try {
port = Integer.parseInt(args[0]);
}
catch (Exception e) {
port = 8080;
}
try {
//监听服务器端口,等待连接请求
server_socket = new ServerSocket(port);
System.out.println("httpServer running on port "
+server_socket.getLocalPort());
..........
..........


◆ 连接处理分线程设计

在分线程httpRequestHandler 类中实现了HTTP 协议的处理,这个类实现了Runnable 接口,代码如下:

class httpRequestHandler implements Runnable
{
final static String CRLF = "\r\n";
Socket socket;
InputStream input;
OutputStream output;
BufferedReader br;
// 构造方法
public httpRequestHandler(Socket socket) throws Exception
{
  this.socket = socket;
  //得到输入输出流
this.input = socket.getInputStream();
  this.output = socket.getOutputStream();
  this.br =
   new BufferedReader(new InputStreamReader(socket.getInputStream()));
}

// 实现Runnable 接口的run()方法
public void run()
{
  try {
   processRequest();
  }
  catch(Exception e) {
   System.out.printlnEnvelope;
  }
}


◆ 构建processRequest()方法来处理信息的接收和发送

作为实现Runnable 接口的主要内容,在run()方法中调用processRequest()方法来处理客户请求内容的接收和服务器返回信息的发送,代码如下:

private void processRequest() throws Exception
{
while(true) {
//读取并显示Web 浏览器提交的请求信息
String headerLine = br.readLine();
System.out.println("The client request is "+ headerLine);
if(headerLine.equals(CRLF) || headerLine.equals("")) break;
//根据请求字符串中的空格拆分客户请求
StringTokenizer s = new StringTokenizer(headerLine);
String temp = s.nextToken();
if(temp.equals("GET")) {
   String fileName = s.nextToken();
   fileName = "." + fileName ;
.............
.............


在processRequest()方法中得到客户端请求后,利用一个StringTokenizer 类完成了字符串的拆分,这个类可以实现根据字符串中指定的分隔符(缺省为空格)将字符串拆分成为字串的功能。利用nextToken()方法依次得到这些字串;sendBytes()方法完成信息内容的发送,contentType()方法用于判断文件的类型。

显示Web页面

显示 Web 页面的index.html 文件代码如下:

<html>
<head>
<meta http-equiv="Content-Language" content="zh-cn">
<meta name="GENERATOR" content="Microsoft FrontPage 5.0">
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>Java Web 服务器</title>
</head>
<body>
<p>********* <font color="#FF0000">欢迎你的到来!</font>*********</p>
<p>这是一个用 Java 语言实现的 Web 服务器</p>
<hr>
</body>
</html>


运行实例

为了测试上述程序的正确性,将编译后的httpServer.class、httpRequestHandler.class和上面的index.html文件置于网络的某台主机的同一目录中。

首先运行服务器程序 java httpServer 8080,服务器程序运行后显示端口信息“httpServer runing on port 8080”, 然后在浏览器的地址栏中输入http://localhost:8080/index.html,就可以正确显示网页,同时在显示“httpServer runing on port 8080 ”窗口中服务器会出现一些信息。

SMTP
电子邮件传递可以由多种协议来实现。目前,在Internet 网上最流行的三种电子邮件协议是SMTP、POP3 和 IMAP,下面分别简单介绍。

◆ SMTP 协议

简单邮件传输协议(Simple Mail Transfer Protocol,SMTP)是一个运行在TCP/IP之上的协议,用它发送和接收电子邮件。SMTP 服务器在默认端口25上监听。SMTP客户使用一组简单的、基于文本的命令与SMTP服务器进行通信。在建立了一个连接后,为了接收响应,SMTP客户首先发出一个命令来标识它们的电子邮件地址。如果SMTP服务器接受了发送者发出的文本命令,它就利用一个OK响应和整数代码确认每一个命令。客户发送的另一个命令意味着电子邮件消息体的开始,消息体以一个圆点“.”加上回车符终止。

◆ POP3 协议

邮局协议(Post Office Protocol Version 3,POP3)提供了一种对邮件消息进行排队的标准机制,这样接收者以后才能检索邮件。POP3服务器也运行在TCP/IP之上,并且在默认端口110上监听。在客户和服务器之间进行了初始的会话之后,基于文本的命令序列可以被交换。POP3客户利用用户名和口令向POP3服务器认证。POP3中的认证是在一种未加密的会话基础之上进行的。POP3客户发出一系列命令发送给POP3服务器,如:请求客户邮箱队列的状态、请求列出的邮箱队列的内容和请求检索实际的消息。POP3代表一种存储转发类型的消息传递服务。现在,大部分邮件服务器都采用SMTP发送邮件,同时使用POP3接收电子邮件消息。

◆ IMAP 协议

Internet 消息访问协议(Internet Message Access Protocol,IMAP)是一种电子邮件消息排队服务,它对POP3的存储转发限制提供了重要的改进。IMAP也使用基于文本命令的语法在TCP/IP上运行,IMAP服务器一般在默认端口143监听。IMAP服务器允许IMAP客户下载一个电子邮件的头信息,并且不要求将整个消息从服务器下载至客户,这一点与POP3是相同的。IMAP服务器提供了一种排队机制以接收消息,同时必须与SMTP相结合在一起才能发送消息。

下面以SMTP发送电子邮件为例讲解怎样用Java 实现SMTP 服务器应用功能,从而完成邮件的发送的。

SMTP 命令

SMTP协议是目前网上流行的发送E-Mail的协议,SMTP协议共有14条命令。不过,发一封E-Mail只需用如下5条命令就足够了,分别为:

◆ HELO <SP> <domain> <CRLF> ,与SMTP服务器握手,传送本机域名;

◆ MAIL <SP> FROM:<reverse-path> <CRLF>,传送发信者的信箱名称;

◆ RCPT <SP> TO:<forward-path> <CRLF>,传送接收者的信箱名称;

◆ DATA <CRLF>,发送信件数据(包括信头和信体);

◆ QUIT <CRLF>,退出与SMTP服务器的连接。

编程思路

首先我们设计一个邮件发送程序的交互界面,界面中包括用户输入邮件的收件人、发信人和主题组件的单行文本框,书写邮件内容的多行文本框等。然后为了能够实现E-mail的发送和设置,我们设计一个SmtpMail类,它封装了与邮件服务器之间的Socket 通信操作,以及SMTP 命令的发送和响应信息的接收。

编程技巧说明

1.设置窗体和组件

我们设计了一个MailSendFrame()类继承Frame 对象,作为容纳组件的主窗体。Main()函数实现将窗体启动时置于屏幕的正中央,窗口定义代码如下:

public static void main(String[] args) {
mailSendFrame mailSendFrame = new mailSendFrame();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = mailSendFrame.getSize();
if (frameSize.height > screenSize.height) {
frameSize.height = screenSize.height;
}
if (frameSize.width > screenSize.width) {
frameSize.width = screenSize.width;
}
mailSendFrame.setLocation((screenSize.width - frameSize.width) / 2,
(screenSize.height - frameSize.height) / 2);
mailSendFrame.setVisible(true);
mailSendFrame.show();
}


在Main()函数中,首先利用代表系统信息的Toolkit对象得到当前系统中设置的屏幕分辨率,并且用分辨率和窗体的大小作比较,然后,调用MailSendFrame的SetLocation()方法设置窗体的左上角坐标,使窗体的中心和屏幕的中心正好重合,从而将窗体居中。

//组件实例变量的定义
Panel panelMain = new Panel();
Panel panelUp = new Panel();
Panel panel3 = new Panel();
Panel panel4 = new Panel();
Panel panel6 = new Panel();
Panel panel7 = new Panel();
TextField txtServer = new TextField();
TextField txtTo = new TextField();
TextField txtFrom = new TextField();
TextField txtSubject = new TextField();
Panel panel8 = new Panel();
Label lblFile = new Label();
Button cmdBrowse = new Button();
Panel panelDown = new Panel();
TextArea txtMail = new TextArea();
Panel panel10 = new Panel();
Button cmdSend = new Button();
Button cmdExit = new Button();
.......
.......
panelMain.add(panelUp, null);
panelUp.add(panel3, null);
panel3.add(new Label("发信服务器:"), null);
panel3.add(txtServer, null);
panelUp.add(panel4, null);
panel4.add(new Label("收件人:"), null);
panel4.add(txtTo, null);
panelUp.add(panel6, null);
panelUp.add(panel7, null);
panel7.add(new Label("主题:"), null);
panel7.add(txtSubject, null);
panel6.add(new Label("发件人:"), null);
panel6.add(txtFrom, null);
panelUp.add(panel8, null);
panel8.add(new Label("附件: "), null);
panel8.add(lblFile, null);
panel8.add(cmdBrowse, null);
panelMain.add(panelDown, null);
panelDown.add(txtMail, BorderLayout.CENTER);
panelDown.add(panel10, BorderLayout.SOUTH);
panel10.add(cmdSend, null);
panel10.add(cmdExit, null);
panelDown.add(new Label(" "), BorderLayout.EAST);
panelDown.add(new Label(" "), BorderLayout.WEST);
........
........


窗体组件的定义都是在Init()方法中完成,设置好收件人、发信人和主题组件的单行文本框,书写邮件内容的多行文本框,以及附件中的浏览按钮、发送和退出按钮。

2.窗体中的事件处理

事件处理也是在Init()方法中完成。选取附件文件的“浏览”按钮的事件处理,在单击该按钮时,打开一个OpenFileDialog 文件对话框,读取用户所选取的文件名。打开文件对话框的“浏览”按钮的代码如下:

private FileDialog openFileDialog= new FileDialog(this,"打开文件",FileDialog.LOAD);
public mailSendFrame() {
try {
Init();
}
catch(Exception e) {
e.printStackTrace();
}
}
......
......


单击“发送”按钮的事件处理,实现用户填写邮件信息的收集和邮件的发送操作。“发送”按钮的代码如下:

cmdSend.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
cmdSend_actionPerformedEnvelope;
}
}


实现cmdSend_actionPerformed()方法如下:

void cmdSend_actionPerformed(ActionEvent e) {
mailSender.setFrom(txtFrom.getText().trim());
mailSender.setTo(txtTo.getText().trim());
mailSender.addHeader("Subject",txtSubject.getText().trim()) ;
mailSender.addData(txtMail.getText()) ;
if(!lblFile.getText().trim().equals("") )
mailSender.addAttachment(lblFile.getText().trim());
mailSender.open(txtServer.getText().trim(),25);
mailSender.transmit();
mailSender.close();
}


单击“退出”按钮的事件处理,实现程序的退出和窗体的关闭。“退出”按钮和侦听器的程序代码如下:

cmdExit.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
cmdExit_actionPerformedEnvelope;
}
}
this.addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(WindowEvent e) {
this_windowClosingEnvelope;
}
}


上面程序分别为退出和窗体注册事件的侦听器或适配器,它们处理各自的交互动作。实现cmdExit_actionPerformed()和this_windowClosing()方法如下:

void cmdExit_actionPerformed(ActionEvent e) {
System.exit(0);
}
void this_windowClosing(WindowEvent e) {
System.exit(0);
}


3.SmtpMail 类的实现

采用Open()方法,建立与邮件服务器之间的TCP/IP 连接,创建套接字,并且得到发送命令所用的输出流Send 和接收服务器相应所用的输入流Rev。Open()方法的代码如下:

public int open(String serverName, int port){
try{
mailSocket = new Socket(serverName, port);
send = new PrintWriter(mailSocket.getOutputStream(), true);
recv = new BufferedReader(new InputStreamReader(mailSocket.getInputStream()));
String s1 = recv.readLine();
char c = s1.charAt(0);
if((c == '4') | (c == '5'))
return 0;
}
catch(Exception e){
return 0;
}
return 1;
}


在SmtpMail 类中,通过Transmit()方法完成发送任务。Transmit()方法的代码如下:

public int transmit(){
boolean flag = true;
//发送HELO 命令
if(domain.length() != 0){
int i = sendString("HELO " + domain);
if(i != 1)
return 0;
}
//发送MAIL FROM 命令(发件人)
if(from.length() != 0){
int j = sendString("MAIL FROM:" + from);
if(j != 1)
return 0;
}
//发送RCPT TO 命令(收件人)
if(to.length() != 0){
int k = sendString("RCPT TO:" + to);
if(k != 1)
return 0;
}
//发送邮件正文(DATA 命令)
if(sendString("DATA") != 1)
return 0;
//发送邮件头信息
for(int l = 0; l < x_set.size(); l += 2){
String s = (String)x_set.elementAt(l);
send.println(s + ": " + x_set.elementAt(l + 1));
}
//发送时间及相关内容格式说明
if(x_set.indexOf("Date") < 0)
send.println("Date: " + (new Date()).toString());
........
........


使用SendString()方法来发送字符串命令,并且接收邮件服务器的响应信息,判断命令是否被接收。返回1表示命令被拒绝执行,返回0表示命令被接受。SendString()方法的代码如下:

private int sendString(String s){
String s1 = "";
try{
send.printlnMoon;
s1 = recv.readLine();
}
catch(Exception e){
System.out.print(s1);
return 0;
}
if(s1.length() == 0)
return 0;
char c = s1.charAt(0);
return !((c == '4') | (c == '5')) ? 1 : 0;
}


使用Close()方法来关闭与服务器之间的套接字连接。该方法发送“QUIT”命令,收到响应消息后,才真正关闭连接。Close()方法的代码如下:

public int close(){
int i = 0;
try{
i += sendString("QUIT");
mailSocket.close();
}
catch(Exception e){
return 0;
}
return i == 0 ? 1 : 0;
}


mailSendFrame.java源程序代码如下:

import java.awt.*;
import java.awt.event.*;
public class mailSendFrame extends Frame {
smtpMail mailSender=new smtpMail();
Panel panelMain = new Panel();
Panel panelUp = new Panel();
Panel panel3 = new Panel();
Panel panel4 = new Panel();
Panel panel6 = new Panel();
Panel panel7 = new Panel();
TextField txtServer = new TextField();
TextField txtTo = new TextField();
TextField txtFrom = new TextField();
TextField txtSubject = new TextField();
Panel panel8 = new Panel();
Label lblFile = new Label();
Button cmdBrowse = new Button();
Panel panelDown = new Panel();
TextArea txtMail = new TextArea();
Panel panel10 = new Panel();
Button cmdSend = new Button();
Button cmdExit = new Button();
private FileDialog openFileDialog
= new FileDialog(this,"打开文件",FileDialog.LOAD);
public mailSendFrame() {
try {
Init();
}
catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
mailSendFrame mailSendFrame = new mailSendFrame();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = mailSendFrame.getSize();
if (frameSize.height > screenSize.height) {
frameSize.height = screenSize.height;
}
if (frameSize.width > screenSize.width) {
frameSize.width = screenSize.width;
}
mailSendFrame.setLocation((screenSize.width - frameSize.width) / 2,
(screenSize.height - frameSize.height) / 2);
mailSendFrame.setVisible(true);
mailSendFrame.show();
}
private void Init() throws Exception {
this.setLayout(new BorderLayout());
panelMain.setLayout(new GridLayout(2,1));
panelUp.setLayout(new GridLayout(6,1));
panel3.setLayout(new FlowLayout());
this.setVisible(true);
.......
.......
//smtpMail.java 的源代码
import java.io.*;
import java.net.Socket;
import java.util.*;
public class smtpMail{
private boolean sendConf=false;
public static final int OK = 1;
public static final int ERROR = 0;
private static final String TEXT = "1";
private static final String TFILE = "2";
private static final String BFILE = "3";
private static final String CPR = "Java 1.0";
private static final String MAILER = "X-Mailer";
private static final int BUFFER_SIZE = 48;
private String DELIMETER;
private String SEPARATOR;
private static final int HOW_LONG = 6;
private static final char SMTP_ERROR_CODE1 = 52;
private static final char SMTP_ERROR_CODE2 = 53;
private static final int fillchar = 61;
private static final String cvt =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
private Socket mailSocket;
private BufferedReader recv;
private PrintWriter send;
private String from;
private String to;
private String domain;
private Vector x_set;
private Vector body;
private Vector attach;
public smtpMail(){
DELIMETER = "";
SEPARATOR = "";
mailSocket = null;
recv = null;
send = null;
from = "";
to = "";
domain = "";
x_set = new Vector();
body = new Vector();
attach = new Vector();
DELIMETER = getId();
SEPARATOR = System.getProperty("file.separator");
}
.........
.........


FTP

FTP(File Transfer Protocol 文件传输协议)是Internet 上用来传送文件的协议。在Internet上通过FTP 服务器可以进行文件的上传(Upload)或下载(Download)。FTP是实时联机服务,在使用它之前必须是具有该服务的一个用户(用户名和口令),工作时客户端必须先登录到作为服务器一方的计算机上,用户登录后可以进行文件搜索和文件传送等有关操作,如改变当前工作目录、列文件目录、设置传输参数及传送文件等。使用FTP可以传送所有类型的文件,如文本文件、二进制可执行文件、图象文件、声音文件和数据压缩文件等。

FTP 命令

FTP 的主要操作都是基于各种命令基础之上的。常用的命令有:

◆ 设置传输模式,它包括ASCⅡ(文本) 和BINARY 二进制模式;

◆ 目录操作,改变或显示远程计算机的当前目录(cd、dir/ls 命令);

◆ 连接操作,open命令用于建立同远程计算机的连接;close命令用于关闭连接;

◆ 发送操作,put命令用于传送文件到远程计算机;mput 命令用于传送多个文件到远程计算机;

◆ 获取操作,get命令用于接收一个文件;mget命令用于接收多个文件。

编程思路

根据FTP 的工作原理,在主函数中建立一个服务器套接字端口,等待客户端请求,一旦客户端请求被接受,服务器程序就建立一个服务器分线程,处理客户端的命令。如果客户端需要和服务器端进行文件的传输,则建立一个新的套接字连接来完成文件的操作。

编程技巧说明

1.主函数设计

在主函数中,完成服务器端口的侦听和服务线程的创建。我们利用一个静态字符串变量initDir 来保存服务器线程运行时所在的工作目录。服务器的初始工作目录是由程序运行时用户输入的,缺省为C盘的根目录。

具体的代码如下:

public class ftpServer extends Thread{
private Socket socketClient;
private int counter;
private static String initDir;
public static void main(String[] args){
if(args.length != 0) {
initDir = args[0];
}else{ initDir = "c:";}
int i = 1;
try{
System.out.println("ftp server started!");
//监听21号端口
ServerSocket s = new ServerSocket(21);
for(;Wink{
//接受客户端请求
Socket incoming = s.accept();
//创建服务线程
new ftpServer(incoming,i).start();
i++;
}
}catch(Exception e){}
}


2. 线程类的设计

线程类的主要设计都是在run()方法中实现。用run()方法得到客户端的套接字信息,根据套接字得到输入流和输出流,向客户端发送欢迎信息。

3. FTP 命令的处理

(1) 访问控制命令

◆ user name(user) 和 password (pass) 命令处理代码如下:

if(str.startsWith("USER")){
user = str.substring(4);
user = user.trim();
out.println("331 Password");}
if(str.startsWith("PASS"))
out.println("230 User "+user+" logged in.");


User 命令和 Password 命令分别用来提交客户端用户输入的用户名和口令。

◆ CWD (CHANGE WORKING DIRECTORY) 命令处理代码如下:

if(str.startsWith("CWD")){
String str1 = str.substring(3);
dir = dir+"/"+str1.trim();
out.println("250 CWD command succesful");
}


该命令改变工作目录到用户指定的目录。

◆ CDUP (CHANGE TO PARENT DIRECTORY) 命令处理代码如下:

if(str.startsWith("CDUP")){
int n = dir.lastIndexOf("/");
dir = dir.substring(0,n);
out.println("250 CWD command succesful");
}


该命令改变当前目录为上一层目录。

◆ QUIT命令处理代码如下:

if(str.startsWith("QUIT")) {
out.println("GOOD BYE");
done = true;
}


该命令退出及关闭与服务器的连接,输出GOOD BYE。

(2) 传输参数命令

◆ Port命令处理代码如下:

if(str.startsWith("PORT")) {
out.println("200 PORT command successful");
int i = str.length() - 1;
int j = str.lastIndexOf(",");
int k = str.lastIndexOf(",",j-1);
String str1,str2;
str1="";
str2="";
for(int l=k+1;l<j;l++){
str1 = str2 + str.charAt(l);
}
for(int l=j+1;l<=i;l++){
str2 = str2 + str.charAt(l);
}
tempPort = Integer.parseInt(str1) * 16 *16 +Integer.parseInt(str2);
}


使用该命令时,客户端必须发送客户端用于接收数据的32位IP 地址和16位 的TCP 端口号。这些信息以8位为一组,使用十进制传输,中间用逗号隔开。

◆ TYPE命令处理代码如下:

if(str.startsWith("TYPE")){
out.println("200 type set");
}


TYPE 命令用来完成类型设置。

(3) FTP 服务命令

◆ RETR (RETEIEVE) 和 STORE (STORE)命令处理的代码

if(str.startsWith("RETR")){
out.println("150 Binary data connection");
str = str.substring(4);
str = str.trim();
RandomAccessFile outFile = new
RandomAccessFile(dir+"/"+str,"r");
Socket tempSocket = new Socket(host,tempPort);
OutputStream outSocket
= tempSocket.getOutputStream();
byte byteBuffer[]= new byte[1024];
int amount;
try{
while((amount = outFile.read(byteBuffer)) != -1){
outSocket.write(byteBuffer, 0, amount);
}
outSocket.close();
out.println("226 transfer complete");
outFile.close();
tempSocket.close();
}
catch(IOException e){}
}
if(str.startsWith("STOR")){
out.println("150 Binary data connection");
str = str.substring(4);
str = str.trim();
RandomAccessFile inFile = new
RandomAccessFile(dir+"/"+str,"rw");
Socket tempSocket = new Socket(host,tempPort);
InputStream inSocket
= tempSocket.getInputStream();
byte byteBuffer[] = new byte[1024];
int amount;
try{
while((amount =inSocket.read(byteBuffer) )!= -1){
inFile.write(byteBuffer, 0, amount);
}
inSocket.close();
out.println("226 transfer complete");
inFile.close();
tempSocket.close();
}
catch(IOException e){}
}


文件传输命令包括从服务器中获得文件RETR和向服务器中发送文件STOR,这两个命令的处理非常类似。处理RETR命令时,首先得到用户要获得的文件的名称,根据名称创建一个文件输入流,然后和客户端建立临时套接字连接,并得到一个输出流。随后,将文件输入流中的数据读出并借助于套接字输出流发送到客户端,传输完毕以后,关闭流和临时套接字。

STOR 命令的处理也是同样的过程,只是方向正好相反。

◆ DELE (DELETE)命令处理代码如下:

if(str.startsWith("DELE")){
str = str.substring(4);
str = str.trim();
File file = new File(dir,str);
boolean del = file.delete();
out.println("250 delete command successful");
}


DELE 命令用于删除服务器上的指定文件。

◆ LIST命令处理代码如下:

if(str.startsWith("LIST")) {
try{
out.println("150 ASCII data");
Socket tempSocket = new Socket(host,tempPort);
PrintWriter out2= new PrintWriter(tempSocket.getOutputStream(),true);
File file = new File(dir);
String[] dirStructure = new String[10];
dirStructure= file.list();
String strType="";
for(int i=0;i<dirStructure.length;i++){
if( dirStructure[i].indexOf(".") == -1) {
strType = "d ";}
else
{strType = "- ";}
out2.println(strType+dirStructure[i]);
}
tempSocket.close();
out.println("226 transfer complete");
}
catch(IOException e){}


LIST 命令用于向客户端返回服务器中工作目录下的目录结构,包括文件和目录的列表。处理这个命令时,先创建一个临时的套接字向客户端发送目录信息。这个套接字的目的端口号缺省为1,然后为当前工作目录创建File 对象,利用该对象的list()方法得到一个包含该目录下所有文件和子目录名称的字符串数组,然后根据名称中是否含有文件名中特有的“.”来区别目录和文件。最后,将得到的名称数组通过临时套接字发送到客户端。

用Java 实现Web 服务器功能主要采用的协议是HTTP 协议,即超文本传输协议,根据HTTP 的工作原理(会话过程)分为四个步骤:连接(Connection)、请求(Request)、应答(Response)和关闭(Close),它们分别为:

1. 对于客户机与服务器建立连接是通过Socket来实现的。采用URL提供信息,URL

http://<IP 地址>/[端口号]/[路径][?<查询信息>]

2. 客户向服务器提出的请求信息是以请求头的形式发送给服务器。请求头包括HTTP 方法和头字段。

HTTP 方法常用的有GET、HEAD 和POST 等;头字段包括有:DATE、PARGMA、ACCEPT、AOTHORIZATION、FORM 、MESSAGE_ID、BEFERRER、 MIME-VERTION 和 USER-AGENT 等。

3. 服务器对请求做出应答消息包含头字段形式的报文信息。报文第一行是状态行。状态行的格式为“<HTTP 版本号><状态代码><解释短语>”。状态码是个三位数字码,分为四类:

◆ 以 2 开头,表示请求被成功处理;

◆ 以 3 开头,表示请求被重定向;

◆ 以 4 开头,表示客户的请求有错;

◆ 以 5 开头,表示服务器不能满足请求。

解释短语是对状态码的解释。

4. 最后是关闭客户与服务器之间的连接。

用Java 实现SMTP 服务器功能可以采用Java Applet、Java Servlet 和 Java Application 来实现。本文的实例选用的是Java Application 来实现SMTP 发送电子邮件功能。在实际应用中使用Java 语言实现电子邮件的接收和发送功能,即MAIL邮件服务器,一般采用基于JavaMail的邮件系统来实现的。通过配置JavaMail,在此基础上进行适当的编程来实现SMTP、POP3 和IMAP等协议,完成邮件服务器的接收和发送邮件任务。

用Java 实现FTP 服务器主要是在 RFC协议基础上来完成FTP 文件传输功能。使用Java 的服务器Socket来实现客户机和服务器的连接,由客户机发出请求,用户输入需要登录的FTP 服务器地址,同时输入用户名和密码。得到服务器的确认后,用户可以在服务器端进行文件操作,实现文件的上传和下载等功能。




关于女孩子从事软件开发的问题

话题树型展开
人气 标题 作者 字数 发贴时间
8854 java 网络编程 cckoan 26850 2002-12-31 10:32
5854 Re:java 网络编程 azwind 19 2003-01-10 23:38
6044 Re:java 网络编程 justinnc 3 2003-01-14 11:33

flat modethreaded modego to previous topicgo to next topicgo to back
  已读帖子
  新的帖子
  被删除的帖子
Jump to the top of page

   Powered by Jute Powerful Forum® Version Jute 1.5.6 Ent
Copyright © 2002-2021 Cjsdn Team. All Righits Reserved. 闽ICP备05005120号-1
客服电话 18559299278    客服信箱 714923@qq.com    客服QQ 714923