Java网络编程之服务端中的Socket

Java提供了ServerSocket类代表服务端的socket,服务端的socket主要工作是在某个端口上等待tcp连接。当远程的客户端尝试连接服务端,服务端就会被“唤醒”,然后协调处一条与客户端的连接并返回一个代表客户端和服务器之间的套接字的Socket对象。

ServerSocket的使用

在Java中,服务器程序的基本生命周期是这样的:

  1. 使用ServerSocket()构造函数在某个端口上创建新的ServerSocket对象。
  2. ServerSocket通过accept()方法监听在该端口上的连接请求。accept()是个阻塞方法,它会一直阻塞直到有客户端尝试建立连接,然后返回一个连接客户和服务器的Socket对象。
  3. 根据服务器的类型,可以使用Socket的getInputStream()方法,getOutputStream()方法来获取与客户端进行通信的输入和输出流。
  4. 服务器和客户端根据约定的协议进行通信,直到连接关闭。
  5. 服务器和客户端都可以关闭连接。
  6. 服务器返回到步骤2并等待下一次连接。

还记得上一篇文章中用到的daytime服务器吗?下面我们实现自己的daytime服务器:

public class MyDaytiemServer {
    public final static int PORT = 13;

    public static void main(String[] args) throws IOException {
        try (ServerSocket server = new ServerSocket(PORT)) {
            System.out.println("Listening on port 13");
            while (true) {
                try (Socket connection = server.accept()) {
                    System.out.println("A request come in...");
                    Writer out = new OutputStreamWriter(connection.getOutputStream());
                    Date now = new Date();
                    out.write(now.toString() + "\r\n");
                    out.flush();
                } catch (IOException e) {}
            }
        }catch (IOException ex) {
            System.err.println(ex);
        }

    }
}

多线程服务器

我们上面的服务器是单线程的,如果有多个请求连接进来,操作系统会把这些请求存储到一个先进先出队列中,这个队列的默认大小为50。一旦这个队列满了,服务器就会拒绝额外的连接请求直到队列不是满的状态。

因此,合理的做法是把每个连接放到一个独立的线程中去处理:

public class MyDaytiemServer {
    public final static int PORT = 13;

    public static void main(String[] args) throws IOException {
        try (ServerSocket server = new ServerSocket(PORT)) {
            System.out.println("Listening on port 13");
            while (true) {
                try{
                    Socket connection = server.accept();
                    Thread t = new DaytimeThread(connection);
                    t.start();
                } catch (IOException e) {}
            }
        }catch (IOException ex) {
            System.err.println(ex);
        }

    }

    private static class DaytimeThread extends Thread{
        private Socket connection;
        public DaytimeThread(Socket connection){
            this.connection = connection;
        }

        @Override
        public void run() {
            try {
                System.out.println("A request come in...");
                Writer out = new OutputStreamWriter(connection.getOutputStream());
                Date now = new Date();
                out.write(now.toString() + "\r\n");
                out.flush();
            }catch (IOException ex){
                System.out.println(ex);
            }finally {
                try{
                    connection.close();
                }catch (Exception e){}
            }

        }
    }
}

好了,现在是多线程了,可以处理并发请求了。但是,服务器能创建的线程数量是有限的,如果同时涌入了大量的请求,也会造成服务器崩溃。因此,考虑使用固定大小的线程池:

public class MyDaytiemServer {
    public final static int PORT = 13;


    public static void main(String[] args) throws IOException {
        ExecutorService pool = Executors.newFixedThreadPool(50);

        try (ServerSocket server = new ServerSocket(PORT)) {
            System.out.println("Listening on port 13");
            while (true) {
                try{
                    Socket connection = server.accept();
                    Runnable task = new DaytimeThread(connection);
                    pool.submit(task);
                } catch (IOException e) {}
            }
        }catch (IOException ex) {
            System.err.println(ex);
        }

    }

    private static class DaytimeThread implements Runnable{
        private Socket connection;
        public DaytimeThread(Socket connection){
            this.connection = connection;
        }

        @Override
        public void run() {
            try {
                System.out.println("A request come in...");
                Writer out = new OutputStreamWriter(connection.getOutputStream());
                Date now = new Date();
                out.write(now.toString() + "\r\n");
                out.flush();
            }catch (IOException ex){
                System.out.println(ex);
            }finally {
                try{
                    connection.close();
                }catch (Exception e){}
            }

        }
    }
}

ServerSocket的构造函数

public ServerSocket(int port) throws BindException, IOException
public ServerSocket(int port, int queueLength) throws BindException, IOException
public ServerSocket(int port, int queueLength, InetAddress bindAddress) throws IOException
public ServerSocket() throws IOException

这些构造函数指定了端口,存储请求的队列大小,本地绑定的地址(对应不同的网卡)

如果主机有多个网卡或IP地址,在默认情况下,服务端socket会在所有的网卡和地址上监听连接请求。如果你想只绑定某个网卡或IP,那么你就可以使用第三个构造函数来指定。

获取ServerSocket的相关信息

public InetAddress getInetAddress()
public int getLocalPort()

如果本机有多个IP地址,getInetAddress会返回其中一个,你无法预测返回的是哪个。

 try {
            ServerSocket server = new ServerSocket(0);
            System.out.println("This server runs on port "
                    + server.getLocalPort());
        } catch (IOException ex) {
            System.err.println(ex);
        }

通过getLocalPort()方法可以得知是在哪个端口上监听。

自己动手实现HTTP服务器

文件服务器

import java.io.*;
import java.net.*;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.util.concurrent.*;
import java.util.logging.*;


public class SingleFileHTTPServer {
    private static final Logger logger = Logger.getLogger("SingleFileHTTPServer");

    private final byte [] content;
    private final byte [] header;
    private final int port;
    private final String encoding;

    public 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) {
        this.content = data;
        this.port = port;
        this.encoding = encoding;
        // 返回的头部
        String header =  "HTTP/1.0 200 OK\r\n"
                + "Server: OneFile 2.0\r\n"
                + "Content-length: " + this.content.length + "\r\n"
                + "Content-type: " + mimeType + "; charset=" + encoding + "\r\n\r\n";
        this.header = header.getBytes(Charset.forName("US-ASCII"));
    }

    public void start() {
        ExecutorService pool = Executors.newFixedThreadPool(100);
        try (ServerSocket server = new ServerSocket(this.port)){
            logger.info("Accepting connections on port " + server.getLocalPort());
            logger.info("Data to be sent:");
            logger.info(new String(this.content, encoding));

            while (true) {
                try {
                    Socket connection = server.accept();
                    pool.submit(new HTTPHandler(connection));
                }catch (IOException ex) {
                    logger.log(Level.WARNING, "Exception accepting connection", ex);
                }catch (RuntimeException ex) {
                    logger.log(Level.SEVERE, "Unexpected error", ex);
                }
            }
        } catch (IOException ex) {
            logger.log(Level.SEVERE, "Could not start server", ex);
        }
    }

    private class HTTPHandler implements Callable<Void> {
        private final Socket connection;

        HTTPHandler(Socket connection) {
            this.connection = connection;
        }

        @Override
        public Void call() throws Exception {
            try {
                OutputStream out = new BufferedOutputStream(connection.getOutputStream());
                InputStream in = new BufferedInputStream(connection.getInputStream());
                // read the first line only; that's all we need
                StringBuilder request = new StringBuilder(80);
                while (true) {
                    int c = in.read();
                    if(c == '\r' || c == '\n' || c == -1) break;;
                    request.append((char)c);
                }
                logger.log(Level.INFO,"Request is: " + request);
                // IF this is HTTP/1.0 or later send a MIME header
                if(request.toString().indexOf("HTTP/") != -1) {
                    out.write(header);
                }
                out.write(content);
                out.flush();
            }catch (IOException ex){
                logger.log(Level.WARNING, "Error writing to client", ex);
            }finally {
                connection.close();
            }
            return null;
        }
    }

    public static void main(String[] args) {
        int port = 80;
        String encoding = "UTF-8";
        String pathStr = "D:\\workspace\\js\\AllBranchs\\5\\ha-web\\coverage\\index.html";
        try {
            Path path = Paths.get(pathStr);
            byte[] data = Files.readAllBytes(path);

            String contentType = URLConnection.getFileNameMap().getContentTypeFor(pathStr);
            SingleFileHTTPServer server = new SingleFileHTTPServer(data, encoding,
                    contentType, port);
            server.start();
        } catch (IOException ex) {
            logger.severe(ex.getMessage());
        }
    }
}

只要用户访问localhost,就给他返回一个固定的文件。

重定向服务器

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.logging.*;
/**
 * Create by yinjingwei on 2018/6/22/022.
 */
public class Redirector {
    private static final Logger logger = Logger.getLogger("Redirector");
    private final int port;
    private final String newSite;
    public Redirector(String newSite, int port) {
        this.port = port;
        this.newSite = newSite;
    }
    public void start() {
        try (ServerSocket server = new ServerSocket(port)) {
            logger.info("Redirecting connections on port "
                    + server.getLocalPort() + " to " + newSite);
            while (true) {
                try {
                    Socket s = server.accept();
                    Thread t = new RedirectThread(s);
                    t.start();
                } catch (IOException ex) {
                    logger.warning("Exception accepting connection");
                } catch (RuntimeException ex) {
                    logger.log(Level.SEVERE, "Unexpected error", ex);
                }
            }
        } catch (BindException ex) {
            logger.log(Level.SEVERE, "Could not start server.", ex);
        } catch (IOException ex) {
            logger.log(Level.SEVERE, "Error opening server socket", ex);
        }
    }
    private class RedirectThread extends Thread {
        private final Socket connection;
        RedirectThread(Socket s) {
            this.connection = s;
        }
        public void run() {
            try {
                Writer out = new BufferedWriter(
                        new OutputStreamWriter(
                                connection.getOutputStream(), "US-ASCII"
                        )
                );
                Reader in = new InputStreamReader(
                        new BufferedInputStream(
                                connection.getInputStream())
                );
                // read the first line only; that's all we need
                StringBuilder request = new StringBuilder(80);
                while (true) {
                    int c = in.read();
                    if (c == '\r' || c == '\n' || c == -1) break;
                    request.append((char) c);
                }
                String get = request.toString();
                String[] pieces = get.split("\\w*");
                String theFile = pieces[1];
                // If this is HTTP/1.0 or later send a MIME header
                if (get.indexOf("HTTP") != -1) {
                    out.write("HTTP/1.0 302 FOUND\r\n"); //302重定向
                    Date now = new Date();
                    out.write("Date: " + now + "\r\n");
                    out.write("Server: Redirector 1.1\r\n");
                    out.write("Location: " + newSite + theFile + "\r\n");
                    out.write("Content-type: text/html\r\n\r\n");
                    out.flush();
                }
                // Not all browsers support redirection so we need to
                // produce HTML that says where the document has moved to.
                out.write("<HTML><HEAD><TITLE>Document moved</TITLE></HEAD>\r\n");
                out.write("<BODY><H1>Document moved</H1>\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<P>");
                out.write("</BODY></HTML>\r\n");
                out.flush();
                logger.log(Level.INFO,
                        "Redirected " + connection.getRemoteSocketAddress());
            } catch(IOException ex) {
                logger.log(Level.WARNING,
                        "Error talking to " + connection.getRemoteSocketAddress(), ex);
            } finally {
                try {
                    connection.close();
                } catch (IOException ex) {}
            }
        }
    }

    public static void main(String[] args) {
        int thePort = 80;
        String theSite = "http://www.baidu.com";
        Redirector redirector = new Redirector(theSite, thePort);
        redirector.start();
    }
}

访问localhost会重定向到百度。

特定用途的HTTP服务器就举例到这里了,接下来是压轴大戏,一个功能基本完整的HTTP服务器,能访问整个网站目录,包括html,js,css等。

五脏俱全的HTTP服务器

麻雀虽小,五脏俱全。

import java.io.*;
import java.net.*;
import java.nio.file.Files;
import java.util.Date;
import java.util.concurrent.*;
import java.util.logging.*;
/**
 * Create by yinjingwei on 2018/6/22/022.
 */
public class JHTTP {
    private static final Logger logger = Logger.getLogger(
            JHTTP.class.getCanonicalName());

    private static final int NUM_THREADS = 50;
    private static final String INDEX_FILE = "index.html";

    private final File rootDirectory;
    private final int port;

    public JHTTP(File rootDirectory, int port) throws IOException {
        if (!rootDirectory.isDirectory()) {
            throw new IOException(rootDirectory + " dose not exist as a dirctory");
        }
        this.rootDirectory = rootDirectory;
        this.port = port;
    }

    public void start() throws IOException {
        ExecutorService pool = Executors.newFixedThreadPool(NUM_THREADS);
        try (ServerSocket server = new ServerSocket(port)) {
            logger.info("Accepting connections on port " + server.getLocalPort());
            logger.info("Document Root: " + rootDirectory);
            while (true) {
                try{
                    Socket request = server.accept();
                    Runnable r = new RequestProcessor(rootDirectory,INDEX_FILE,request);
                    pool.submit(r);
                }catch (IOException ex){
                    logger.log(Level.WARNING,"Error accepting connection",ex);
                }
            }
        }
    }

    public static void main(String[] args) {
        File docroot;
        try {
            docroot = new File(args[0]);
        }catch (ArrayIndexOutOfBoundsException ex){
            System.out.println("Usage: java JHTTP docroot port");
            return;
        }

        int port;
        try{
            port = Integer.parseInt(args[1]);
            if(port <0 || port > 65535) port = 8888;
        }catch (RuntimeException ex){
            port = 8888;
        }

        try {
            JHTTP webserver = new JHTTP(docroot, port);
            webserver.start();
        } catch (IOException ex) {
            logger.log(Level.SEVERE, "Server could not start", ex);
        }
    }

}

class RequestProcessor implements Runnable{
    private final static Logger logger = Logger.getLogger(
            RequestProcessor.class.getCanonicalName());

    private File rootDirectory;
    private String indexFileName = "index.html";
    private Socket connection;

    public RequestProcessor(File rootDirectory,String indexFileName,Socket connection) {
        if (rootDirectory.isFile()) {
            throw new IllegalArgumentException(
                    "rootDirectory must be a directory, not a file");
        }
        try {
            rootDirectory = rootDirectory.getCanonicalFile();
        }catch (IOException ex){}
        this.rootDirectory = rootDirectory;

        if (indexFileName != null) this.indexFileName = indexFileName;
        this.connection = connection;
    }
    @Override
    public void run() {
        String root = rootDirectory.getPath();
        try {
            OutputStream raw = new BufferedOutputStream(connection.getOutputStream());
            Writer out = new OutputStreamWriter(raw);
            Reader in = new InputStreamReader(new BufferedInputStream(connection.getInputStream()),
                    "UTF-8");
            StringBuilder requestLine = new StringBuilder();
            while (true) {
                int c = in.read();
                if (c == '\r' || c == '\n') break;
                requestLine.append((char)c);
            }

            String get = requestLine.toString();
            logger.info(connection.getRemoteSocketAddress() + " " + get);
            String [] tokens = get.split("\\s+");
            String method = tokens[0];
            String version = "";
            if (method.equals("GET")) {
                String fileName = tokens[1];
                if (fileName.endsWith("/")) fileName += indexFileName;
                String contentType = URLConnection.getFileNameMap().getContentTypeFor(fileName);
                 if (fileName.endsWith(".css")) {
                    contentType = "text/css"; // 修复css无法解析的问题
                }
                if (tokens.length >2) {
                    version = tokens[2];
                }

                File theFile = new File(rootDirectory,fileName.substring(1,fileName.length()));

                if (theFile.canRead()
                        // 禁止访问根目录之外的文件
                        && theFile.getCanonicalPath().startsWith(root)) {
                    byte [] theData = Files.readAllBytes(theFile.toPath());
                    if (version.startsWith("HTTP/")) {
                        sendHeader(out,"HTTP/1.0 200 OK",contentType,theData.length);
                    }

                    // 发送文件
                    raw.write(theData);
                    raw.flush();

                } else {
                    String body = new StringBuilder("<HTML>\r\n").
                            append("<HEAD><TITLE>File Not Found</TITLE>\r\n")
                            .append("</HEAD>\r\n")
                            .append("<BODY>")
                            .append("<H1>HTTP Error 404: File Not Found</H1>\r\n")
                            .append("</BODY></HTML>\r\n").toString();
                    if (version.startsWith("HTTP/")) {
                        sendHeader(out, "HTTP/1.0 404 File Not Found",
                                "text/html; charset=utf-8", body.length());
                    }
                    out.write(body);
                    out.flush();
                }
            } else { // method dose not equal "GET"
                String body = new StringBuilder("<HTML>\r\n")
                        .append("<HEAD><TITLE>Not Implemented</TITLE>\r\n")
                        .append("</HEAD>\r\n")
                        .append("<BODY>")
                        .append("<H1>HTTP Error 501: Not Implemented</H1>\r\n")
                        .append("</BODY></HTML>\r\n").toString();
                if (version.startsWith("HTTP/")) { // send a MIME header
                    sendHeader(out, "HTTP/1.0 501 Not Implemented",
                            "text/html; charset=utf-8", body.length());
                }
                out.write(body);
                out.flush();
            }
        }catch (IOException ex) {
            logger.log(Level.WARNING,"Error talking to " + connection.getRemoteSocketAddress(), ex);
        }finally {
            try{
                connection.close();
            }
            catch (IOException ex) {}
        }
    }
    private void sendHeader(Writer out,String responseCode,String contentType,int length) throws IOException{
        out.write(responseCode + "\r\n");
        Date now = new Date();
        out.write("Date: " + now + "\r\n");
        out.write("Server: JHTTP 2.0\r\n");
        out.write("Content-length: " + length + "\r\n");
        out.write("Content-type: " + contentType + "\r\n\r\n");
        out.flush();
    }
}

这个服务器暂时只能处理get请求,但是用作静态网页的服务器已经足够了。
由于css文件getContentTypeFor会解析为text/plain。导致css样式无法解析,可通过下面的代码解析:

 if (fileName.endsWith(".css")) {
                    contentType = "text/css"; // 修复css无法解析的问题
                }
java进行客户端的applet (小程序)开发的技术已广为使用,而用java进行服务器端的servlet(服务器小程序)开发则尚需揭开其神秘的面纱,本书正是基于这样的目的编写的。全书从java服务器的体系结构、开发工具和管理工具、编程技术、安全机制等四个方面全面介绍java服务器的升友技术。通过阅读本书,读者不仅能够知道用java服务器体系结构开发servlet与用传统cgi编写程序的好处,而且还能够掌握编写java servlet的各种技术和技巧。    本书适用于所有对java和web感兴趣的读者使用和参考。 第1部分 java服务器基础[/font] [/font] 第1章 java计算的体系结构[/font] [/font] 1.1 传统的计算模式[/font] 1.1.1 集式计算模式[/font] 1.1.2 胖客户机/服务器计算模式[/font] 1.1.3 瘦客户机/服务器计算模式[/font] 1.1.4 java desktop与x终端[/font] 1.2 java计算[/font] 1.3 小结[/font] [/font] 第2章 java服务器与servlet[/font] [/font] 2.1 java server的结构[/font] 2.1.1 服务[/font] 2.1.2 服务器[/font] 2.1.3 服务构架[/font] 2.1.4 服务器处理构架[/font] 2.1.5 http服务[/font] .2.1.6 核心servlet[/font] 2 1.7 acl[/font] 2.2 servlet及其功能[/font] 2.2.1 什么是servlet[/font] 2.2.2 servlet与applet的区别[/font] 2.2.3 servlet的应用[/font] 2.3 servlet与cgi的比较[/font] 2.3.1 cgi[/font] 2.3.2 servlet的优点[/font] 2.4 运行servlet[/font] 2.5 小结[/font] [/font] 第2部分 java服务器的开发工具和管理工具[/font] [/font] 第3章 使用java server与servlet开发工具[/font] [/font] 3.1 java web server的安装与启动[/font] 3.1.1 在windows 95或windows nt上安装[/font] 3.1.2 在unix上安装[/font] 3.1.3 java server环境变量的设置[/font] 3.1.4 启动java server[/font] 3.1.5 在端口80上启动java server[/font] 3.1.6 结束java serve的运行[/font] 3.1.7 删除java server[/font] 3.1.8 显示缺省主页[/font] 3.1.9 使用随机文档[/font] 3.2 servlet的加载和调用[/font] 3.2.1 加载servlet[/font] 3.2.2 servlet的标识[/font] 3.2.3 调用servlet[/font] 3.3 jsdk的安装与使用[/font] 3.3.1 servletrunner[/font] 3.3.2 在netscape服务器上安装jsdk[/font] 3.3.3 在apache服务器上安装[/font] 3.4 小结[/font] [/font] 第4章 java web server的管理[/font] [/font] 4.1 进入管理工具[/font] 4.2 设置[/font] 4.2.1 设置web service[/font] 4.2.2 设置proxy service[/font] 4.2.3 设置管理服务[/font] 4.3 监视[/font] 4.3.1 记录输出[/font] 4.3.2 记录统计[/font] 4.3.3 资源的使用[/font] 4.4 安全控制[/font] 4.4.1 用户管理[/font] 4.4.2 用户组[/font] 4.4.3 访问控制表[/font] 4.4.4 资源保护[/font] 4.5 servlet管理[/font] 4.5.1 增加servlet[/font] 4.5.2 设置servlet属性[/font] 4.5.3 修改servlet属性[/font] 4.5.4 删除servlet[/font] 4.6 小结[/font] [/font] 第3部分 java服务器编程[/font] [/font] 第5章 servlet包介绍[/font] [/font] 5.1 javax.servlet包[/font] 5.1.1 接口servlet[/font] 5.1.2 接口servletconfig[/font] 5.1.3 接口servletcontext[/font] 5.1.4 接口servletrequest[/font] 5.1.5 接口servletresponse[/font] 5.1.6 类genericservlet[/font] 5.1.7 类servletinputstream[/font] 5.1.8 类servletoutputstream[/font] 5.2 javax.servlet.http包[/font] 5.2.1 接口httpservletrequest[/font] 5.2.2 接口httpservletresponse[/font] 5.2.3 类httpservlet[/font] 5.2.4 类httputils[/font] 5.3 sun.servlet包介绍[/font] 5.3.1 接口servletconnection[/font] 5.3.2 类servletloader[/font] 5.4 小结[/font] [/font] 第6章 sun.servlet.http包介绍[/font] [/font] 6.1 类cookie[/font] 6.2 类httpdate[/font] 6.3 类httpinputstream[/font] 6.4 类httpoutputstream[/font] 6.5 类httprequest[/font] 6.6 类httpresponse[/font] 6.7 类httpserver[/font] 6.8 类httpserverhandler[/font] 6.9 类httpservletconfig[/font] 6.10 类messagebytes[/font] 6.11 类messagestring[/font] 6.12 类mimeheaderfield[/font] 6.13 类mimeheaders[/font] 6.14 小结[/font] [/font] 第7章 genericservlet编程[/font] [/font] 7.1 servlet编程的一个小例子[/font] 7.1.1 genericservlet在jsdk的地位[/font] 7.1.2 从hello world学习genericservlet编程[/font] 7.1.3 servlet的两个生命周期函数[/font] 7.2 servlet的对象[/font] 7.2.1 servlet的请求和应答对象[/font] 7.2.2 selvlet的servletconfig对象[/font] 7.3 servlet输出html文件[/font] 7.4 小结[/font] [/font] 第8章 httpservlet编程介绍[/font] [/font] 8.1 http简介[/font] 8.1.1 http协议基本概念及其特点[/font] 8.1.2 http协议的请求和应答[/font] 8.1.3 http协议的信息处理方法[/font] 8.2 httpservlet编程入门[/font] 8.2.1 一个简单例子[/font] 8.2.2 httpservlet的常用对象介绍[/font] 8.3 cookie编程介绍[/font] 8.3.1 cookie简介[/font] 8.3.2 一个cookie例子程序[/font] 8.4 小结[/font] [/font] 第9章 servlet高级编程[/font] [/font] 9.1 servlet与form[/font] 9.1.1 form简介[/font] 9.1.2 一个完整的form[/font] 9.2 编写一个shtml文本[/font] 9.3 异常处理[/font] 9.4 servlet与applet的通信[/font] 9.5 小结[/font] [/font] 第10章 聊天室[/font] [/font] 10.1 “聊天室”客户程序[/font] 10.2 “聊天室”服务器程序[/font] 10.3 “聊天”[/font] 10.4 小结[/font] [/font] 第11章 rmi与servlet[/font] [/font] 11.1 rmi概述[/font] 11.2 如何编写rmi[/font] 11.2.1 定义一个远程接口[/font] 11.2.2 实现远程接口[/font] 11.2.3 写一个applet[/font] 11.2.4 写html文本[/font] 11.2.5 编译和执行[/font] 11.3 rmi与servlet[/font] 11.3.1 定义远程接口[/font] 11.3.2 实现远程接口[/font] 11.3.3 调用rmi的servlet[/font] 11.3.4 调用servlet的applet[/font] 11.3.5 三个超文本文件[/font] 11.3.6 编译和执行[/font] 11.4 小结[/font] [/font] 第12章 servlet与jdbc[/font] [/font] 12.1 jdbc简介[/font] 12.1.1 实现jdbc[/font] 12.1.2 jdbc类细节[/font] 12.2 jdbc与servlet结合[/font] 12.2.1 基本过程[/font] 12.2.2 servlet的同步[/font] 12.2.3 应用举例[/font] 12.3 小结[/font] [/font] 第13章 开发服务[/font] [/font] 13.1 服务[/font] 13.2 实现服务的核心[/font] 13.3 安装服务[/font] 13.3.1 创建属性文件[/font] 13.3.2 创建jamfile[/font] 13.4 创建日志[/font] 13.4.1 日志文件类型[/font] 13.4.2 在自己的服务加入日志文件[/font] 13.5 服务的参数管理[/font] 13.5.1 系统参数[/font] 13.5.2 增加新的参数[/font] 13.5.3 创建存放参数的域[/font] 13.5.4 实现访问方法[/font] 13.5.5 构造器[/font] 13.5.6 update方法[/font] 13.6 使用realm来创建安全服务[/font] 13.6.1 查找realm[/font] 13.6.2 设置realm[/font] 13.6.3 收集用户数据的对象[/font] 13.6.4 给服务增加身份验证代码[/font] 13.7 小结[/font] [/font] 第14章 pagecompile[/font] [/font] 14.1 生成动态主页[/font] 14.1.1 创建第一个主页[/font] 14.1.2 从html文档到java源文件[/font] 14.2 语法[/font] 14.2.1 声明变量[/font] 14.2.2 条件语句[/font] 14.2.3 循环语句[/font] 14.2.4 注释[/font] 14.2.5 使用backquotes[/font] 14.2.6 使用servlet标记[/font] 14.2.7 输出java表达式[/font] 14.3 使用java类[/font] 14.3.1 重用java类[/font] 14.3.2 创建自己的类[/font] 14.3.3 访问request和response对象[/font] 14.4 servlet初始化参数[/font] 14.5 pagecompile使用的标记[/font] 14.6 小结[/font] [/font] 第4部分 java服务器安全机制[/font] [/font] 第15章 java web server的安全机制[/font] [/font] 15.1 安全综述[/font] 15.1.1 常见的攻击形式[/font] 15.1.2 java web server的安全机制[/font] 15.1.3 其他特点[/font] 15.1.4 unix上的独有特点[/font] 15.2 web realm机制[/font] 15.2.1 realm模型[/font] 15.2.2 用户和授权[/font] 15.2.3 用户组[/font] 15.3 访问控制表(acl)[/font] 15 3.1 访问控制表结构[/font] 15.3.2 计算权限的规则[/font] 15.3.3 例子[/font] 15.3.4 用法举例[/font] 15.4 ssl机制[/font] 15.4.1 什么是ssl[/font] 15.4.2 建立安全的web服务[/font] 15.4.3 使用认证许可[/font] 15.5 沙盒[/font] 15.6 小结[/font]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愤怒的可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值