一、 內容概述
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