run( )
run( )方法在服务器线程启动时被调用。它在指定端口生成一个新的ServerSocket,在服务器套接字处进入一个调用accept( )的无限循环,把结果Socket传给doRequest( )作检查。
start( ) AND stop( )
存在两个用来启动和终止服务器过程的方法。这些方法设置stopFlag的值。
main( )
你可以在命令行用main( )方法来运行应用程序。它为服务器自身设置LogMessage参数,然后为log( )提供简单的控制台输出执行。
代码 下面是httpd的源代码:
import java.net.*; import java.io.*; import java.text.*; import java.util.*; class httpd implements Runnable, LogMessage { private intport; privateString docRoot; privateLogMessage log; privateHashtable cache = new Hashtable(); privateboolean stopFlag; privatestatic String version = "1.0"; privatestatic String mime_text_html = "text/html"; privatestatic String CRLF = "\r\n"; privatestatic String indexfile = "index.html"; privatestatic int buffer_size = 8192; staticString mt[] = { // mapping from file extto Mime-Type "txt", "text/plain", "html", mime_text_html, "htm", "text/html", "gif", "image/gif", "jpg","image/jpg", "jpeg", "image/jpg", "class","application/octet-stream" }; staticString defaultExt = "txt"; staticHashtable types = new Hashtable(); static { for (inti=0; i types.put(mt[i],mt[i+1]); } staticString fnameToMimeType(String filename) { if(filename.endsWith("/")) // special for index files. returnmime_text_html; int dot =filename.lastIndexOf('.'); Stringext = (dot > 0) ? filename.substring(dot + 1) : defaultExt; Stringret = (String) types.get(ext); returnret != null ? ret : (String)types.get(defaultExt); } inthits_served = 0; intbytes_served = 0; intfiles_in_cache = 0; intbytes_in_cache = 0; int hits_to_cache= 0; privatefinal byte toBytes(String s)[] { byte b[]= s.getBytes(); return b; } privateMimeHeader makeMimeHeader(String type, int length) { MimeHeader mh = new MimeHeader(); DatecurDate = new Date(); TimeZonegmtTz = TimeZone.getTimeZone("GMT"); SimpleDateFormat sdf = newSimpleDateFormat("dd MMM yyyy hh:mm:ss zzz"); sdf.setTimeZone(gmtTz); mh.put("Date", sdf.format(curDate)); mh.put("Server", "JavaCompleteReference/" + version); mh.put("Content-Type", type); if(length >= 0) mh.put("Content-Length", String.valueOf(length)); returnmh; } privateString error(int code, String msg, String url) { Stringhtml_page = "" + CRLF + " " + code + " " + msg +" " + CRLF; if(url != null) html_page += "Error when fetching URL: " + url + CRLF; html_page+= "" + CRLF; MimeHeader mh = makeMimeHeader(mime_text_html, html_page.length()); HttpResponsehr = new HttpResponse(code, msg, mh); logEntry("GET", url, code, 0); return hr+ html_page; } // Read'in' until you get two \n's in a row. // Returnup to that point as a String. // Discardall \r's. privateString getRawRequest(InputStream in) throwsIOException { bytebuf[] = new byte[buffer_size]; intpos=0; int c; while ((c= in.read()) != -1) { switch(c) { case'\r': break; case'\n': if(buf[pos-1] == c) { return new String(buf,0,pos); } default: buf[pos++] = (byte) c; } } returnnull; } staticString months[] = { "Jan", "Feb", "Mar", "Apr","May", "Jun", "Jul", "Aug", "Sep", "Oct","Nov", "Dec" }; privateString host; // fmt02dis the same as C's printf("%02d", i) privatefinal String fmt02d(int i) { if(i <0) { i = -i; return((i < 9) ? "-0" : "-") + i; } else { return((i < 9) ? "0" : "") + i; } } privatevoid logEntry(String cmd, String url, int code, int size) { Calendarcalendar = Calendar.getInstance(); int tzmin= calendar.get(Calendar.ZONE_OFFSET)/(60*1000); inttzhour = tzmin / 60; tzmin -=tzhour * 60; log.log(host + " - - [" + fmt02d(calendar.get(Calendar.DATE) ) + "/" + months[calendar.get(Calendar.MONTH)] + "/" + calendar.get(Calendar.YEAR) + ":" + fmt02d(calendar.get(Calendar.HOUR) ) + ":" + fmt02d(calendar.get(Calendar.MINUTE) ) +":" + fmt02d(calendar.get(Calendar.SECOND)) + " " + fmt02d(tzhour) + fmt02d(tzmin) + "]\"" + cmd +" " + url +" HTTP/1.0\" " + code +" " + size +"\n"); hits_served++; bytes_served += size; } privatevoid writeString(OutputStream out, String s) throwsIOException { out.write(toBytes(s)); } privatevoid writeUCE(OutputStream out, UrlCacheEntry uce) throwsIOException { HttpResponse hr = new HttpResponse(200,"OK", uce.mh); writeString(out, hr.toString()); out.write(uce.data, 0, uce.length); logEntry("GET", uce.url, 200, uce.length); } privateboolean serveFromCache(OutputStream out, String url) throwsIOException { UrlCacheEntry uce; if ((uce= (UrlCacheEntry)cache.get(url)) != null) { writeUCE(out, uce); hits_to_cache++; returntrue; } returnfalse; } privateUrlCacheEntry loadFile(InputStream in, String url, MimeHeader mh) throwsIOException { UrlCacheEntry uce; bytefile_buf[] = new byte[buffer_size]; uce = newUrlCacheEntry(url, mh); int size= 0; int n; while ((n= in.read(file_buf)) >= 0) { uce.append(file_buf, n); size +=n; } in.close(); cache.put(url, uce); files_in_cache++; bytes_in_cache += uce.length; returnuce; } privateUrlCacheEntry readFile(File f, String url) throwsIOException { if(!f.exists()) returnnull; InputStream in = new FileInputStream(f); intfile_length = in.available(); Stringmime_type = fnameToMimeType(url); MimeHeader mh = makeMimeHeader(mime_type, file_length); UrlCacheEntry uce = loadFile(in, url, mh); returnuce; } privatevoid writeDiskCache(UrlCacheEntry uce) throwsIOException { Stringpath = docRoot + uce.url; Stringdir = path.substring(0, path.lastIndexOf("/")); dir.replace('/', File.separatorChar); newFile(dir).mkdirs(); FileOutputStream out = new FileOutputStream(path); out.write(uce.data, 0, uce.length); out.close(); } // A clientasks us for a url that looks like this: // http://the.internet.site/the/url // we goget it from the site and return it... privatevoid handleProxy(OutputStream out, String url, MimeHeader inmh) { try { intstart = url.indexOf("://") + 3; intpath = url.indexOf('/', start); Stringsite = url.substring(start, path).toLowerCase(); intport = 80; Stringserver_url = url.substring(path); int colon = site.indexOf(':'); if (colon > 0) { port = Integer.parseInt(site.substring(colon+ 1)); site = site.substring(0, colon); } url = "/cache/" + site + ((port!= 80) ? (":" + port) :"") + server_url; if(url.endsWith("/")) url+= indexfile; if(!serveFromCache(out, url)) { if(readFile(new File(docRoot + url), url) != null) { serveFromCache(out, url); return; } // Ifwe haven't already cached this page, open a socket // tothe site's port and send a GET command to it. // Wemodify the user-agent to add ourselves... "via". Socket server = new Socket(site, port); InputStream server_in = server.getInputStream(); OutputStream server_out = server.getOutputStream(); inmh.put("User-Agent", inmh.get("User-Agent") + " via JavaCompleteReferenceProxy/" + version); String req = "GET " + server_url + " HTTP/1.0" +CRLF + inmh + CRLF; writeString(server_out, req); String raw_request = getRawRequest(server_in); HttpResponse server_response = newHttpResponse(raw_request); writeString(out, server_response.toString()); if(server_response.statusCode == 200) { UrlCacheEntry uce = loadFile(server_in, url, server_response.mh); out.write(uce.data, 0, uce.length); writeDiskCache(uce); logEntry("GET", site + server_url, 200, uce.length); } server_out.close(); server.close(); } } catch(IOException e) { log.log("Exception: " + e); } } privatevoid handleGet(OutputStream out, String url, MimeHeader inmh) { bytefile_buf[] = new byte[buffer_size]; Stringfilename = docRoot + url + (url.endsWith("/") ? indexfile : ""); try { if(!serveFromCache(out, url)) { Filef = new File(filename); if (!f.exists()) { writeString(out, error(404, "Not Found", filename)); return; } if (!f.canRead()) { writeString(out, error(404, "Permission Denied", filename)); return; } UrlCacheEntryuce = readFile(f, url); writeUCE(out, uce); } } catch(IOException e) { log.log("Exception: " + e); } } privatevoid doRequest(Socket s) throws IOException { if(stopFlag) return; InputStreamin = s.getInputStream(); OutputStream out = s.getOutputStream(); Stringrequest = getRawRequest(in); int fsp =request.indexOf(' '); int nsp =request.indexOf(' ', fsp+1); int eol =request.indexOf('\n'); Stringmethod = request.substring(0, fsp); Stringurl = request.substring(fsp+1, nsp); Stringraw_mime_header = request.substring(eol + 1); MimeHeader inmh = new MimeHeader(raw_mime_header); request =request.substring(0, eol); if(method.equalsIgnoreCase("get")) { if(url.indexOf("://") >= 0) { handleProxy(out, url, inmh); } else{ handleGet(out, url, inmh); } } else { writeString(out, error(405, "Method Not Allowed", method)); } in.close(); out.close(); } public voidrun() { try { ServerSocket acceptSocket; acceptSocket = new ServerSocket(port); while(true) { Socket s = acceptSocket.accept(); host= s.getInetAddress().getHostName(); doRequest(s); s.close(); } } catch(IOException e) { log.log("accept loop IOException: " + e + "\n"); } catch(Exception e) { log.log("Exception: " + e); } } privateThread t; public synchronizedvoid start() { stopFlag= false; if (t ==null) { t = newThread(this); t.start(); } } publicsynchronized void stop() { stopFlag= true; log.log("Stopped at " + new Date() + "\n"); } publichttpd(int p, String dr, LogMessage lm) { port = p; docRoot = dr; log = lm; } // Thismain and log method allow httpd to be run from the console. publicstatic void main(String args[]) { httpd h =new httpd(80, "c:\\www", null); h.log = h; h.start(); try{ Thread.currentThread().join(); } catch(InterruptedException e) {}; } public voidlog(String m) { System.out.print(m); } }
HTTP.java
这里是一个给HTTP服务器一个“前面板”功能的applet类。该applet有两个可以用来配置服务器的参数:port和docroot。这是一个很简单的小应用程序。它创建了一个httpd实例,把它作为LogMessage 接口传入自己。然后创建了两个面板。一个面板在顶部有一个简单的标签,在中部是一个TextArea 文本区以显示LogMessage;另一个面板在底部有两个按钮及一个标签。小应用程序的start( )和stop( )方法调用httpd相应的方法。 标注有 “Start” 和 “Stop”的按钮调用httpd中它们相应的方法。在任何时候只要一条消息被记录,右下角的Label对象就更新,这样它包含httpd最新的统计数据。
import java.util.*; import java.applet.*; import java.awt.*; import java.awt.event.*; public class HTTP extendsApplet implements LogMessage, ActionListener { private int m_port = 80; private String m_docroot ="c:\\www"; private httpd m_httpd; privateTextArea m_log; privateLabel status; privatefinal String PARAM_port = "port"; privatefinal String PARAM_docroot = "docroot"; publicHTTP() { } public voidinit() { setBackground(Color.white); Stringparam; // port:Port number to listen on param =getParameter(PARAM_port); if (param != null) m_port = Integer.parseInt(param); //docroot: web document root param =getParameter(PARAM_docroot); if (param!= null) m_docroot = param; setLayout(new BorderLayout()); Label lab= new Label("Java HTTPD"); lab.setFont(new Font("SansSerif", Font.BOLD, 18)); add("North", lab); m_log =new TextArea("", 24, 80); add("Center", m_log); Panel p =new Panel(); p.setLayout(new FlowLayout(FlowLayout.CENTER,1,1)); add("South", p); Buttonbstart = new Button("Start"); bstart.addActionListener(this); p.add(bstart); Buttonbstop = new Button("Stop"); bstop.addActionListener(this); p.add(bstop); status =new Label("raw"); status.setForeground(Color.green); status.setFont(new Font("SansSerif", Font.BOLD, 10)); p.add(status); m_httpd =new httpd(m_port, m_docroot, this); } public voiddestroy() { stop(); } public voidpaint(Graphics g) { } public voidstart() { m_httpd.start(); status.setText("Running "); clear_log("Log started on " + new Date() +"\n"); } public voidstop() { m_httpd.stop(); status.setText("Stopped "); } public voidactionPerformed(ActionEvent ae) { Stringlabel = ae.getActionCommand(); if(label.equals("Start")) { start(); } else { stop(); } } public voidclear_log(String msg) { m_log.setText(msg +"\n"); } public voidlog(String msg) { m_log.append(msg); status.setText(m_httpd.hits_served + " hits (" + (m_httpd.bytes_served / 1024) + "K)," + m_httpd.files_in_cache + " cached files (" + (m_httpd.bytes_in_cache /1024) + "K), " + m_httpd.hits_to_cache + " cached hits"); status.setSize(status.getPreferredSize()); } }
注意:httpd.java 和HTTP.java文件中,代码是在假定文件根部是“c:\www”的基础上建立的。你可能需要改变配置的值。因为这个小应用程序写入一个日志文件,它只能在被信任的条件下工作。例如,一个小应用程序在它可以获得用户CLASSPATH时被信任。
|
对于你的大多数网络需求,你会对TCP/IP型网络很满意。它提供了有序的、可预测和可靠的信息包数据流。但是,这样做的代价也很大。TCP包含很多在拥挤的网络中处理拥塞控制的复杂算法以及信息丢失的悲观的预测。这导致了一个效率很差的传输数据方式。数据报是一种可选的替换方法。
数据报(Datagrams)是在机器间传递的信息包,它有些像从一个训练有素但是很盲目的捕手投出一记有力的传球给三垒。一旦数据报被释放给它们预定的目标,不保证它们一定到达目的地,甚至不保证一定存在数据的接收者。同样,数据报被接受时,不保证它在传输过程不受损坏,不保证发送它的机器仍在那儿等待响应。
Java通过两个类实现UDP协议顶层的数据报:DatagramPacket对象是数据容器,DatagramSocket是用来发送和接受DatagramPackets的机制。
|
DatagramPackets 可以用四个构造函数中的一个创建。第一个构造函数指定了一个接收数据的缓冲区和信息包的容量大小。它通过DatagramSocket接收数据。第二种形式允许你在存储数据的缓冲区中指定一个偏移量。第三种形式指定了一个用于DatagramSocket决定信息包将被送往何处的目标地址和端口。第四种形式从数据中指定的偏移量位置开始传输数据包。想象前两种形式是建立在“盒内”的,后两种形式形成了填塞物,并为一个信封注明了地址。下面是四个构造函数:
DatagramPacket(byte data[ ], int size)
DatagramPacket(byte data[ ], int offset, int size)
DatagramPacket(byte data[ ], int size, InetAddressipAddress, int port)
DatagramPacket(byte data[ ], int offset, int size,InetAddress ipAddress, int port)
存在几种方法可获取DatagramPacket内部状态。 它们对信息包的目标地址和端口号以及原始数据和数据长度有完全的使用权,下面是它们的概述:
InetAddress getAddress( ) 返回目标文件InetAddress,一般用于发送。
Int getPort( ) 返回端口号。
byte[ ] getData( ) 返回包含在数据包中的字节数组数据。多用于在接收数据之后从数据包来检索数据。
Int getLength( ) 返回包含在将从getData( )方法返回的字节数组中有效数据长度。通常它与整个字节数组长度不等。
|
下面的例子实现了一个非常简单的联网通信的客户和服务器。消息被写入服务器的窗口并通过网络写入客户端,在此它们被显示。
// Demonstrate Datagrams. import java.net.*; class WriteServer { publicstatic int serverPort = 666; publicstatic int clientPort = 999; publicstatic int buffer_size = 1024; publicstatic DatagramSocket ds; publicstatic byte buffer[] = new byte[buffer_size]; publicstatic void TheServer() throws Exception { intpos=0; while(true) { int c =System.in.read(); switch(c) { case-1: System.out.println("Server Quits."); return; case'\r': break; case'\n': ds.send(new DatagramPacket(buffer,pos, InetAddress.getLocalHost(),clientPort)); pos=0; break; default: buffer[pos++] = (byte) c; } } } publicstatic void TheClient() throws Exception { while(true) { DatagramPacket p = new DatagramPacket(buffer, buffer.length); ds.receive(p); System.out.println(new String(p.getData(),0, p.getLength())); } } publicstatic void main(String args[]) throws Exception { if(args.length == 1) { ds =new DatagramSocket(serverPort); TheServer(); } else { ds =new DatagramSocket(clientPort); TheClient(); } } }
该程序被DatagramSocket 构造函数限制在本地机的两个端口间运行。试着运行该程序,在一个窗口中键入
java WriteServer
这是在客户端的。然后运行
java WriteServer 1
这是在服务器端的。在服务器窗口中打印的任何东西在接受回车之后将被送到客户窗口。
注意:该例题需要你的计算机与Internet相连。
|
给你5个类InetAddress,Socket,ServerSocket,DatagramSocket和DatagramPacket,你可以编写现有的任何Internet协议。它们同样为Internet连接提供功能强大的低级控制。Java 的网络包也完美地反映了Internet的状态。
|
1. 思考现实生活中,我们有没有处理过一组相同的类型数据的概念?
2. 如果你学过数学上矩阵的概念,那么它与多维数组的关系如何?
|
在本章中,我们主要学习了:
u 线程的概念;
u Socket通讯协议的建立;
|
英文 全文 中文
Socket Socke 套接字,使应用程序能够读写与收发通讯协定与资料的程序
|
目前的网络聊天工具很多,但是功能上不能符合每个人的意愿;现在你的朋友要求你做一个有特殊要求的聊天工具;要求你先用Java做一个简单的Socket通讯的测试,作为前期的准备;后续还要加入一些特殊的功能;你能不能单独完成这个简单Socket协议的初期“架设”工作;