想做一个简单的服务器,用来更好的了解web交互模式。
http1.0 简单服务器 :三个文件
package com.httpserver;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class HttpResponse extends Thread {
Socket socket;
JTextArea ta;
PrintStream pout;
boolean isHttp1 = false;
String path = null;
HttpResponse(Socket socket, JTextArea ta,JTextField path)
{
this.socket = socket;
this.ta = ta;
this.path = path.getText();
}
public void run()
{
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pout = new PrintStream(socket.getOutputStream());
String requestLine = br.readLine();
//String requestCmds = ""+requestLine;
if(requestLine==null){
error(400,"Empty Request");
}
else{
ta.append("Http请求,来自:["+socket.getInetAddress()+":"+socket.getPort()+"] "+requestLine+"\n");
}
if(requestLine.toLowerCase().indexOf("http/1.")!=-1){
isHttp1 = true;
}
String[] request = requestLine.split(" ");
if(request.length<2)
{
error(400,"Bad Request");
}
String str1 = request[0];
if(str1.equals("GET")){
serveFile(request[1]);
}
else{
error(400, "Bad Request");
ta.append("Bad Request");
}
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
return;
}
}
private void error(int erorcd, String erortx) {
erortx = "<html><h1>" + erortx + "</h1></html>";
if (isHttp1) {
pout.println("HTTP/1.0 " + erorcd + " " + erortx);
pout.println("Content-type: text/html");
pout.println("Content-length: " + erortx.length() + "\n");
}
pout.println(erortx);
}
private void serveFile(String requestPath) {
if (requestPath.equals("/")){
/**
* 取首页文件,首页文件可以为index.html或index.htm
*/
requestPath = "/index.html";
if(path==null){
path=new File("").getAbsolutePath();
}
if(!new File(path+requestPath).exists()){
requestPath="/index.htm";
}
}
try {
sendFileData(requestPath);
ta.append("文件传输成功 ! "+requestPath+"\n");
} catch (Exception e) {
error(404, "");
ta.append("请求文件不存在\n");
}
}
private void sendFileData(String requestPath) throws IOException,FileNotFoundException {
InputStream inputstream = new FileInputStream(path+requestPath);
if (inputstream == null)
throw new FileNotFoundException(requestPath);
if (isHttp1) {
pout.println("HTTP/1.0 200 Document follows");
pout.println("Content-length: " + inputstream.available());
if (requestPath.endsWith(".gif"))
pout.println("Content-type: image/gif");
else if (requestPath.endsWith(".jpg"))
pout.println("Content-type: image/jpeg");
else if (requestPath.endsWith(".png") || requestPath.endsWith(".htm"))
pout.println("Content-Type: text/png");
else if (requestPath.endsWith(".css") || requestPath.endsWith(".htm"))
pout.println("Content-Type: text/css");
else if (requestPath.endsWith(".js") || requestPath.endsWith(".htm"))
pout.println("Content-Type: text/javascript");
else if (requestPath.endsWith(".html") || requestPath.endsWith(".htm"))
pout.println("Content-Type: text/html");
else
pout.println("Content-Type: application/octet-stream");
pout.println("Connection: Keep-Alive");//println 表示写完自带换行
/*****下面这个换行回车,必须要有 这个是报头 格式******/
pout.println();
/***** 但是这个 只需要一个换行回车就行了 **/
//pout.println();
}
/*缓冲区设为8K*/
byte[] is = new byte[8*1024];
/*实际测试 缓冲区 太小了,请求一张 大于 8K 的图片就 无法传输了,好像不是这个问题*/
//byte[] is = new byte[300*1024];
int length=0;
while((length=inputstream.read(is))!=-1){
pout.write(is, 0, length);
}
inputstream.close();
pout.flush();
}
}
package com.httpserver;
import java.net.*;
import javax.swing.*;
public final class ServerThread extends Thread {
int port;
JTextArea display;
JTextField status;
JTextField direc;
//JTextField ipAdd;
ServerSocket listener = null;
ServerThread (int port, JTextArea display, JTextField status,JTextField direc,JTextField ipAdd) throws Exception
{
this.port = port;
this.display = display;
this.status = status;
this.direc = direc;
byte[] ip = new byte[4];
String ipp = ipAdd.getText();
//System.out.println(ipp);
String[] ip1 = ipp.split("\\.");
for(int i = 0;i<ip1.length;i++){
try{
ip[i] = (byte) Integer.parseInt(ip1[i]);
}
catch(Exception e){
ip[i] = -1;
}
//System.out.println(ip1[i]);
}
//System.out.println(ip[0]+" "+ip[1]+" "+ip[2]+" "+ip[3]);
//this.ipAdd = ipAdd;
try{
listener = new ServerSocket(port,20, InetAddress.getByAddress(ip));
}
catch(Exception e){
listener = new ServerSocket(port,20);
display.append("无效的IP地址,已使用默认地址:"+InetAddress.getLocalHost().getHostAddress()+"\n");
ipAdd.setText(InetAddress.getLocalHost().getHostAddress());
}
display.append("服务器已开启,端口:"+port+"\n");
status.setText("服务器已开启。 "+ipAdd.getText()+":"+port+" "+direc.getText());
}
public void run()
{
while(true)
{
try{
Socket socket = listener.accept();
/*for(String s = new BufferedReader(new InputStreamReader(socket.getInputStream())).readLine();s!="\n";
s = new BufferedReader(new InputStreamReader(socket.getInputStream())).readLine()){
display.append(new BufferedReader(new InputStreamReader(socket.getInputStream())).readLine());
}
*/
/*BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
for(String s = br.readLine();s!=null;s = br.readLine()){
display.append(s+"\n");
}*/
new HttpResponse(socket, display, direc).start();
}
catch(Exception e){
display.append(e+"\n");
}
}
}
}
package com.httpserver;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.synth.SynthStyle;
public class Surface implements ActionListener {
JFrame jf = new JFrame("Web Server");
JLabel iP = new JLabel("IP: ");
JLabel port = new JLabel("Port: ");
JTextField ipAdd = new JTextField(15);
JTextField portNum = new JTextField(4);
JButton start = new JButton("Start");
JButton over = new JButton("Stop");
JLabel direct = new JLabel("Main Directory: ");
JTextField directory = new JTextField(24);
JButton br = new JButton("...");
// JLabel diary = new JLabel("Diary:");
JTextArea ta = new JTextArea(10, 40);
// JTextField outDirct = new JTextField(15);
// JTextField outIp = new JTextField(8);
JTextField outStatus = new JTextField(40);
// JLabel status = new JLabel("Status:");
ServerThread listener = null;
boolean hasStarted = false;
String ipp;
public void init() {
JPanel jp1 = new JPanel();
jp1.add(iP);
jp1.add(ipAdd);
jp1.add(port);
jp1.add(portNum);
jp1.add(start);
jp1.add(over);
JPanel jp2 = new JPanel();
jp2.add(direct);
jp2.add(directory);
jp2.add(br);
// JPanel jp3 = new JPanel();
// jp3.add(diary);
// JScrollPane jsp = new JScrollPane();
// jsp.add(ta);
Border bb = BorderFactory.createEtchedBorder();
Border tb1 = BorderFactory.createTitledBorder(bb, "Console");
Border tb2 = BorderFactory.createTitledBorder(bb, "Diary");
JScrollPane jsp = new JScrollPane(ta);
JPanel jp4 = new JPanel();
jp4.add(jsp);
jp4.setBorder(tb2);
ta.setLineWrap(true);
ta.setEditable(false);
start.addActionListener(this);
over.addActionListener(this);
br.addActionListener(this);
Box jb1 = Box.createVerticalBox();
jb1.add(jp1);
jb1.add(jp2);
jb1.setBorder(tb1);
directory.setText("/Users/hello/Sites/testwebsites");
// Box jb2 = Box.createVerticalBox();
// jb2.add(jp3);
// jb2.add(jp4);
Box jb = Box.createVerticalBox();
// jb.setBorder(bb);
outStatus.setBackground(jf.getBackground());
outStatus.setBorder(null);
outStatus.setEditable(false);
JPanel jp5 = new JPanel();
jp5.setBorder(bb);
// jp5.add(status);
jp5.add(outStatus);
outStatus.setText("服务器已停止");
try {
ipAdd.setText(InetAddress.getLocalHost().getHostAddress());
} catch (Exception e) {
ipAdd.setText("0.0.0.0");
}
jb.add(jb1);
jb.add(jp4);
jb.add(jp5);
// jb.add(jp3);
// jb.add(jp4);
jf.add(jb);
// jf.add(jp1);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setResizable(false);
jf.pack();
jf.setVisible(true);
}
public void startServer() {
start.setEnabled(false);
over.setEnabled(true);
int port = 8080;
try {
port = Integer.parseInt(portNum.getText());
} catch (Exception e) {
ta.append("端口号异常,已使用8080端口。\n");
portNum.setText("8080");
}
if (hasStarted && port == listener.port) {
listener.resume();
ta.append("服务器已开启,端口:" + listener.port + "\n");
outStatus.setText("服务器已开启。" + ipp + ":" + port + " " + directory.getText());
// ipAdd.setText(InetAddress.getLocalHost().getHostAddress());
return;
}
try {
listener = new ServerThread(port, ta, outStatus, directory, ipAdd);
ipp = ipAdd.getText();
} catch (Exception e) {
ta.append("端口已被其他程序占用,请重试。\n");
listener = null;
hasStarted = false;
}
if (listener == null) {
start.setEnabled(true);
} else {
hasStarted = true;
listener.start();
}
}
public void exitServer() {
ta.append("服务器关闭。\n");
if (listener != null) {
listener.stop();
}
System.exit(0);
}
public void stopServer() {
start.setEnabled(true);
over.setEnabled(false);
listener.suspend();
ta.append("服务器已停止。\n");
outStatus.setText("服务器已停止。");
}
public void selectPath() {
String str = "";
String loc = "";
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new java.io.File("."));
chooser.setDialogTitle("主目录");
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
chooser.setAcceptAllFileFilterUsed(false);
if (chooser.showOpenDialog(jf) == JFileChooser.APPROVE_OPTION) {
// str += chooser.getCurrentDirectory();
str += chooser.getSelectedFile();
loc = str.replaceAll("\\\\", "/"); //// Windows路径到JAVA路径的转换
}
directory.setText(loc);
}
public void actionPerformed(ActionEvent event) {
String command = event.getActionCommand();
if (command.equals("Start")) {
startServer();
}
if (command.equals("Stop")) {
stopServer();
}
if (command.equals("Exit")) {
exitServer();
}
if (command.equals("...")) {
selectPath();
}
}
public static void main(String[] args) {
Surface sf = new Surface();
sf.init();
// sf.outStatus.setText("服务器已开启:220.111.332.444:8000 sdafsagassasa");
String a = new String("源码下载站");
System.out.println("16制"+strTo16(a));
char c = '8';
System.out.printf("The value of char %c %X,is %d.%n", c, (int)c,(int)c);
//16进制 转成10进制。 java 内部编码是 utf-16
String str = String.valueOf(c);
byte[] bys;
try {
bys = a.getBytes("UTF-8");
for (int i = 0; i < bys.length; i++) {
System.out.printf("%X ", bys[i]);
}
//因为是Unicode编码,所以编码前有字节序。
//(byte & 0xFF) 这是一个字节相与,然后 << 8 代表数据 左移 8位 ,相当于 两字节大小 与后面的 或运算
// 相当于 地位的字节 复制了 新添加的 字节
//int unicode = (bys[2] & 0xFF) << 8 | (bys[3]& 0xFF);
//System.out.printf("The unicode value of %c is %d.%n", c, unicode);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static String strTo16(String s) {
String str = "";
System.out.println("字符串长度"+s.length());
for (int i = 0; i < s.length(); i++) {
int ch = (int) s.charAt(i);
String s4 = Integer.toHexString(ch);
str = str + s4;
}
return str;
}
}
参考自:GitHub
另,Tomcat源码剖析电子书:
代码和UML图:https://github.com/Aresyi/HowTomcatWorks
排版更好的百度电子书:https://yuedu.baidu.com/ebook/ac92f0d35122aaea998fcc22bcd126fff7055d60
网络编程资料:
自定义 服务器 css 设置成了 二进制类型 导致加载 不出来 样式,报文头和体 有且只有空一行 才能 被 浏览器 正确 解析。空两行 浏览器 加载不了 图片
http://localhost/~cool/beike/index.html
http://www.ietf.org/rfc/rfc1945.txt
http编码
https://blog.csdn.net/hongxingxiaonan/article/details/49963643
tcp连接详解:
https://blog.csdn.net/liuxinmingcode/article/details/50376035
tcp包长度
https://blog.csdn.net/lishanmin11/article/details/77045745
http 长度
https://blog.csdn.net/zerooffdate/article/details/78962818
http详解
https://segmentfault.com/a/1190000006689767#articleHeader8
https://blog.csdn.net/u012813201/article/details/70211255
websocket
https://www.jianshu.com/p/f666da1b1835
http://www.cnblogs.com/hustskyking/p/websocket-with-node.html
https://www.cnblogs.com/oshyn/p/3574497.html