一、 内容概述
1.实验目的
1) 掌握网络应用程序的开发方法;2) 掌握Client/ Server结构软件的设计与开发方法;3) 掌握Socket机制的工作原理。
2. 实验前的准备
1) 阅读教材关于TCP/IP协议和Socket的相关内容;2) 阅读WinSock编程指南;3) 阅读本实验所附内容;4) 熟悉VC++、C#或Java开发语言。
3.实验内容
1)利用Java语言,基于TCP编写一个简单的Web Server,要求可以实现单用户简单页面浏览;2)修改上述Web Server,实现多用户同时连接(多线程)请求。
4.实验要求
1) 实现Web 服务器能发送文本文档和图片文件,Web 客户端能接收服务器响应发来的文本文件和图片文件,并能判断通信时发生的一些简单的错误。
2) 在实验报告中要说明实现Web Server的主要步骤、关键类和作用、记录实验过程和实验结果。
二、 设计要点及解决方案
本实验采用Java网络编程技术开发;在服务器端和客户端分别使用ServerSocket 和 Socket 来创建面向字节流、面向连接的TCP 套接字;在服务器端采用多线程来处理各个客户的连接。模拟HTTP请求·应答模型进行 Web 服务器与Web客户机间的通信。对文本文件与图片文件数据的传输分别字符输入输出流和字节输入输出流,以便于处理。(文本文档仅限于 *.html 和 *.htm)
服务器程序的基本流程是:
1) 创建服务器端套接字并调用其accept( )等待接受客户端的连接请求。
2) 当与新的客户端连接之后,创建并启动一个新的线程来处理这个新的连接。主线程继续等待接受其他客户端的连接。
3) 与客户端进行通信为客户端提供服务。首先,接受客户端发来的HTTP请求消息;然后,判断该请求是否正确,若错误,则继续判断是请求消息的书写格式不正确还是服务器端找不到请求的文件错误,若正确,则继续判断请求的文件类型:文本文件或图片文件;最后,根据判断的结果向客户端发送HTTP应答消息。
4) 关闭输入输出流、关闭套接字,继续等待接受其他客户端的连接。
客户端程序的基本流程是:
1) 创建与服务器端连接的套接字。
2) 从标准输入接受用户输入的URL,向Web 服务器发送请求该URL代表的资源的请求消息。
3) 接受服务器发来的应答消息并进行处理。首先,根据应答消息状态行和首部判断应答消息的类型,若状态行的原因短语为“错误请求”或“文件找不到”,则置BadRequireFlag 为1,退出程序;否则,然后,根据"Content-Type:"判断应答主体的类型:字符流或字节流,根据判断结果启用相应的输入输出流读写数据。
4) 退出程序。
三、 源代码
1 /** 2 * @(#)HttpServer.java 3 * 4 * HttpServer application 5 * 6 * @author 邝翼飞 7 * @version 1.00 2010/12/15 8 */ 9 10 import java.io.*; 11 import java.net.*; 12 import java.util.*; 13 import java.text.*; 14 15 public class HttpServer 16 { 17 18 public static void main(String[] args) 19 { 20 String root="D:\\httpserver"; 21 int port=800; 22 int count=20; 23 ServerSocket SvrSock; 24 25 try 26 { 27 SvrSock=new ServerSocket(port,count); 28 System.out.println("Server has started. Begin listening..."); 29 30 while(true) 31 { 32 Socket Client=SvrSock.accept(); 33 InetAddress clientAddress=Client.getInetAddress(); 34 System.out.println("Client: "+clientAddress.toString()+ " has connected"); 35 serviceThread st=new serviceThread(Client,root); 36 st.start(); 37 } 38 } 39 catch(IOException e) 40 { 41 System.out.println("Server Exception !"); 42 e.printStackTrace(); 43 } 44 } 45 } 46 47 class serviceThread extends Thread 48 { 49 private String root; 50 private File file; 51 private final String NEWLINE="\r\n"; 52 private String requestfile; 53 private String FileType; 54 private Socket client; 55 private InetAddress cAddress; 56 private BufferedReader in; 57 private PrintStream out; 58 private DataOutputStream out2; 59 private int errflag=-1; 60 61 public serviceThread(Socket s,String root) 62 { 63 client=s; 64 cAddress=s.getInetAddress(); 65 this.root=root; 66 FileType=".html"; 67 } 68 69 public void run() 70 { 71 try 72 { 73 in=new BufferedReader(new InputStreamReader(client.getInputStream())); 74 out=new PrintStream(client.getOutputStream(),true); 75 76 System.out.println("来自客户端的请求消息:"); 77 System.out.println("-----------------------------------"); 78 String type=in.readLine(); 79 System.out.println(type); 80 String sss=new String(""); 81 char ch[]={1,2,3,4,5}; 82 while(true) 83 { 84 sss=in.readLine(); 85 if(sss.equals(String.valueOf(ch))) 86 break; 87 System.out.println(sss); 88 } 89 System.out.println("-----------------------------------"); 90 91 int testtype=testType(type); 92 if(testtype==-1) 93 { 94 if(errflag==0) 95 { 96 System.out.println("客户端请求格式错误"); 97 sendHead(400,FileType,0); 98 } 99 else 100 { 101 System.out.println("文件未找到"); 102 sendHead(404,FileType,0); 103 } 104 } 105 else 106 { 107 try 108 { 109 FileInputStream fins=new FileInputStream(requestfile); 110 long filesize=fins.available(); 111 switch(testtype) 112 { 113 case 0: 114 BufferedReader fin=new BufferedReader(new FileReader(requestfile)); 115 if(fins!=null) 116 { 117 sendHead(200,FileType,filesize); 118 119 String ss=fin.readLine(); 120 while(ss!=null) 121 { 122 out.println(ss); 123 ss=fin.readLine(); 124 } 125 //out.println(NEWLINE); 126 out.println(String.valueOf(ch)); 127 out.flush(); 128 fins.close(); 129 fin.close(); 130 } 131 break; 132 case 1: 133 DataInputStream fin2=new DataInputStream(new BufferedInputStream(fins)); 134 out2=new DataOutputStream(new BufferedOutputStream(client.getOutputStream())); 135 136 if(fins!=null) 137 { 138 sendHead(200,FileType,filesize); 139 140 int count=0; 141 int i; 142 while((i=fin2.read())!=-1) 143 { 144 out2.write(i); 145 count++; 146 } 147 out2.flush(); 148 fins.close(); 149 out2.close(); 150 System.out.println("Bytes number: "+count); 151 } 152 break; 153 default: break; 154 } 155 } 156 catch(FileNotFoundException e) 157 { 158 System.out.println("文件未找到"); 159 sendHead(404,FileType,0); 160 } 161 } 162 163 System.out.println("数据响应已完成\n"); 164 client.close(); 165 } 166 catch(IOException e) 167 { 168 System.out.println("run() service exception !"); 169 e.printStackTrace(); 170 } 171 } 172 173 public int testType(String filetype) 174 { 175 int type=-1; 176 177 filetype=filetype.substring(3,filetype.length()-8).trim(); // 获取请求的Url 178 if((!filetype.startsWith("/")) && (!filetype.startsWith("\\"))) 179 { 180 errflag=0; 181 return type; 182 } 183 requestfile=root+filetype; 184 requestfile=requestfile.replace('/','\\'); 185 if(filetype.indexOf('.')!=-1) 186 { 187 FileType=filetype.substring(filetype.lastIndexOf('.')); 188 boolean istxt=FileType.endsWith(".html") || FileType.endsWith(".htm"); 189 boolean ispic=FileType.endsWith(".gif") || FileType.endsWith(".jpg") || FileType.endsWith(".bmp") || FileType.endsWith(".jpeg"); 190 if(istxt) 191 type=0; 192 else if(ispic) 193 type=1; 194 } 195 else 196 errflag=0; 197 return type; 198 } 199 200 public void sendHead(int code,String type,long size) 201 { 202 String message; 203 String state="OK"; 204 message="HTTP/1.1 "; 205 206 switch(code) 207 { 208 case 200: 209 state=" 请求成功"; break; 210 case 400: 211 state=" 错误请求"; break; 212 case 404: 213 state=" 文件找不到"; break; 214 default: 215 state=" OK"; break; 216 } 217 message += String.valueOf(code) + state + NEWLINE; 218 219 SimpleDateFormat df=new SimpleDateFormat("E yyyy-MM-dd HH:mm:ss"); 220 message +="Date: " +df.format(new Date()) + NEWLINE; 221 222 if(FileType.equals(".jpg") || FileType.equals(".gif") || FileType.equals(".jpeg") || FileType.equals(".bmp")) 223 message += "Content-Type: image/"+FileType.substring(1)+NEWLINE; 224 else 225 message += "Content-Type: text/"+FileType.substring(1)+NEWLINE; 226 227 message += "Content-Length: "+String.valueOf(size)+NEWLINE+NEWLINE; 228 out.print(message); 229 out.flush(); 230 } 231 }
1 /** 2 * @(#)HttpClient.java 3 * 4 * HttpClient application 5 * 6 * @author 邝翼飞 7 * @version 1.00 2010/12/15 8 */ 9 10 import java.io.*; 11 import java.net.*; 12 import java.util.*; 13 14 15 public class HttpClient 16 { 17 private String root; 18 private BufferedReader in; 19 private BufferedWriter out; 20 private DataInputStream in2; 21 private DataOutputStream out2; 22 private PrintStream txtFile; 23 private Boolean head; 24 25 public static void main(String[] args) 26 { 27 HttpClient httpclient; 28 if(args.length==1) 29 httpclient=new HttpClient(args[0],800,"D:\\httpclient"); 30 else 31 httpclient=new HttpClient("localhost",800,"D:\\httpclient"); 32 System.out.print("请输入URL:"); 33 String urlstr=new Scanner(System.in).nextLine(); 34 httpclient.send(urlstr); 35 System.out.println("来自服务器的应答"); 36 System.out.println("-----------------------------------"); 37 httpclient.response(); 38 } 39 40 public HttpClient(String address,int port,String root) 41 { 42 try 43 { 44 this.root=root; 45 head=true; 46 Socket socket=new Socket(address,port); 47 in=new BufferedReader(new InputStreamReader(socket.getInputStream())); 48 out=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); 49 in2=new DataInputStream(new BufferedInputStream(socket.getInputStream())); 50 System.out.println("与服务器的连接已建立."); 51 } 52 catch(IOException e) 53 { 54 System.out.println("Create socket to server failed !"); 55 e.printStackTrace(); 56 System.exit(1); 57 } 58 } 59 60 public void send(String Url) 61 { 62 String ss=null; 63 try 64 { 65 if(Url.equals("/") || Url.equals("/index.html") || Url.equals("www.kuangyifei.com") 66 || Url.equals("http://www.kuangyifei.com")) 67 ss="GET /index.html HTTP/1.1\r\n"; 68 else if(Url.startsWith("www.kuangyifei.com/") || Url.startsWith("http://www.kuangyifei.com/")) 69 ss="GET " + Url.substring(Url.indexOf('/')) + " HTTP/1.1\r\n"; 70 else 71 ss="GET " + Url + " HTTP/1.1\r\n"; 72 73 ss+="Accept: text/html\r\n"; 74 ss+="Accept: image/gif, image/x-xbitmap, image/jpg, image/jpeg\r\n"; 75 ss+="Host: www.kuangyifei.com\r\n"; 76 ss+="Connection: Keep-Alive\r\n"; 77 out.write(ss); 78 char ch[]={1,2,3,4,5}; 79 out.write(String.valueOf(ch)+"\r\n"); 80 out.flush(); 81 } 82 catch(IOException e) 83 { 84 System.out.println("Send Exception !"); 85 e.printStackTrace(); 86 } 87 } 88 89 public void response() 90 { 91 String line,ss; 92 StringTokenizer st; 93 String type="text"; 94 int i; 95 96 try 97 { 98 File TxtFile; 99 File PicFile; 100 BufferedWriter outfile; 101 int contentlength=0; 102 int BadRequireFlag=0; 103 while((line=in.readLine())!=null) 104 { 105 if(line.equals("")) 106 if(head) 107 head=false; 108 if(head) 109 { 110 System.out.println(line); 111 st=new StringTokenizer(line); 112 ss=st.nextToken(); 113 if(ss.equals("HTTP/1.1")) 114 { 115 ss=st.nextToken();ss=st.nextToken(); 116 if(ss.equals("错误请求") || ss.equals("文件找不到")) 117 BadRequireFlag=1; 118 } 119 if(ss.equals("Content-Type:")) 120 { 121 ss=st.nextToken(); 122 i=ss.indexOf('/'); 123 String substr=ss.substring(0,i); 124 if(substr.equals("text")) 125 type="text"; 126 else 127 { 128 type="pic"; 129 String substr2=ss.substring(i+1); 130 PicFile=new File(root,"PicFile."+substr2) ; 131 out2=new DataOutputStream(new FileOutputStream(PicFile)); 132 } 133 } 134 if(ss.equals("Content-Length:")) 135 { 136 ss=st.nextToken(); 137 contentlength=Integer.parseInt(ss); 138 } 139 140 } 141 else if(BadRequireFlag==1) 142 { 143 System.out.println("-----------------------------------"); 144 System.exit(1); 145 } 146 else 147 { 148 if(type.equals("text")) 149 { 150 TxtFile=new File("D:\\httpclient","TxtFile.html") ; 151 outfile=new BufferedWriter(new FileWriter(TxtFile)); 152 int count=0; 154 char ch[]={1,2,3,4,5}; 156 while(line!=null) 157 { 158 System.out.println(line); 159 outfile.append(line+"\r\n"); 160 outfile.flush(); 161 line=in.readLine(); 162 if(line.equals(String.valueOf(ch))) 163 break; 164 } 165 System.out.println("-----------------------------------"); 166 System.out.println("数据已成功传输完毕"); 167 outfile.close();in.close(); 168 break; 169 } 170 else if(type.equals("pic")) 171 { 172 int ch=in2.read(); 173 while(ch!=-1) 174 { 175 out2.write(ch); 176 ch=in2.read(); 177 } 178 out2.flush(); 179 in2.close(); 180 out2.close(); 181 System.out.println("-----------------------------------"); 182 System.out.println("数据已成功传输完毕"); 183 break; 184 } 185 186 } 187 } 188 } 189 catch(IOException e) 190 { 191 System.out.println("Response exception !"); 192 e.printStackTrace(); 193 } 194 } 195 }
四、 运行截图
说明:运行的过程是:开启服务器端, 开启四个客户端,四个客户端分别向服务器端发送请求,请求的格式分别是:根文件夹中存在的文本文件、根文件夹中存在的图片文件、不正确的格式、根文件夹中 不存在的文件。另外由于电脑不够,只能在本机测试,所以所有的客户端的IP地址均为:127.0.0.1。
- 服务器端运行截图:
- 客户端运行截图:
客户端1:IP地址 127.0.0.1
客户端2:IP地址 127.0.0.1
客户端3:IP地址 127.0.0.1
客户端4:IP地址 127.0.0.1