java實現POP3郵件客戶端
完整項目包:http://yunpan.cn/QiVGqacT6uNAj (提取碼:05b4)
第一步:利用socket編程,在客戶端與服務器端之間建立TCP連接,POP3默認端口號為110;
第二步:通過pop3定義的各種命令,用戶可以操作自己的郵箱。
注:
POP3協議中有三種狀態,認正狀態,處理狀態,和更新狀態。 命令的執行可以改變協議的狀態,而對於具體的某命令,它只能在具體的某狀態下使用,這些請參看表1和RFC193。
客戶機與服務器剛與服務器建立連接時,它的狀態為認證狀態;一旦客戶機提供了自己身份並被成功地確認,即由認可狀態轉入處理狀態; 在完成相應的操作后客戶機發出QUIT命令(具體說明見后續內容),則進入更新狀態,更新之后又重返認可狀態;當然在認可狀態下執行QUIT命令,可釋放連接。狀態間的轉移如圖 1所示。
---建立連接---|認可|--認證成功--|處理|--執行QUIT--|更新|
|_______ -QUIT結束_________________|
常用命令:
命令 |
參數 |
使用在何種狀態中 |
描述 |
USER |
Username |
認證 |
此命令與下面的pass命令若成功,將導致狀態轉換 |
PASS |
Password |
認證 |
此命令若成功,狀態轉化為更新 |
APOP |
Name,Digest |
認證 |
Digest是MD5消息摘要 |
STAT |
None |
處理 |
請求服務器發回關於郵箱的統計資料,如郵件總數和總字節數 |
UIDL |
[Msg#](郵件號,下同) |
處理 |
返回郵件的唯一標識符,POP3會話的每個標識符都將是唯一的 |
LIST |
[Msg#] |
處理 |
返回郵件的唯一標識符,POP3會話的每個標識符都將是唯一的 |
RETR |
[Msg#] |
處理 |
返回由參數標識的郵件的全部文本 |
DELE |
[Msg#] |
處理 |
服務器將由參數標識的郵件標記為刪除,由QUIT命令執行 |
TOP |
[Msg#] |
處理 |
服務器將返回由參數標識的郵件的郵件頭+前n行內容,n必須是正整數 |
NOOP |
None |
處理 |
服務器返回一個肯定的響應,用於測試連接是否成功 |
QUIT |
None |
處理、認證 |
1) 如果服務器處於“處理”狀態,么將進入“更新”狀態以刪除任何標記為刪除的郵件,並重返“認證”狀態。 2) 如果服務器處於“認證”狀態,則結束會話,退出連接 |
代碼:
1 /** 2 * @author hewenwu 3 * 這個程序實現了基於POP3協議的郵件接收功能 4 * */ 5 package mail; 6 7 import java.io.BufferedReader; 8 import java.io.BufferedWriter; 9 import java.io.IOException; 10 import java.io.InputStreamReader; 11 import java.io.OutputStreamWriter; 12 import java.io.UnsupportedEncodingException; 13 import java.net.Socket; 14 import java.net.UnknownHostException; 15 import java.util.StringTokenizer; 16 17 public class POP3Client { 18 19 private Socket socket = null; 20 21 private boolean debug=true; 22 23 public static void main(String[] args) throws UnknownHostException, IOException { 24 25 String server="pop3.163.com";//POP3服務器地址 26 27 String user="**********";//用戶名,填寫自己的郵箱用戶名 28 29 String password="*********";//密碼,填寫自己的密碼 30 31 POP3Client pop3Client=new POP3Client(server,110); 32 33 pop3Client.recieveMail(user,password); 34 } 35 36 /*構造函數*/ 37 public POP3Client(String server,int port) throws UnknownHostException, IOException{ 38 try{ 39 40 socket=new Socket(server,port);//在新建socket的時候就已經與服務器建立了連接 41 42 }catch(Exception e){ 43 44 e.printStackTrace(); 45 46 }finally{ 47 48 System.out.println("建立連接!"); 49 } 50 } 51 52 53 //接收郵件程序 54 public boolean recieveMail(String user,String password){ 55 56 try { 57 BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream())); 58 59 BufferedWriter out=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); 60 61 user(user,in,out);//輸入用戶名 62 63 System.out.println("user 命令執行完畢!"); 64 65 pass(password,in,out);//輸入密碼 66 67 System.out.println("pass 命令執行完畢!"); 68 69 stat(in,out); 70 71 System.out.println("stat 命令執行完畢!"); 72 73 list(in,out); 74 75 System.out.println("list 命令執行完畢!"); 76 77 retr(2,in,out); 78 79 System.out.println("retr 命令執行完畢!"); 80 81 quit(in,out); 82 83 System.out.println("quit 命令執行完畢!"); 84 85 }catch(Exception e){ 86 87 e.printStackTrace(); 88 89 return false; 90 } 91 return true; 92 } 93 94 //得到服務器返回的一行命令 95 public String getReturn(BufferedReader in){ 96 97 String line=""; 98 99 try{ 100 line=in.readLine(); 101 102 if(debug){ 103 104 System.out.println("服務器返回狀態:"+line); 105 } 106 }catch(Exception e){ 107 108 e.printStackTrace(); 109 } 110 return line; 111 } 112 113 //從返回的命令中得到第一個字段,也就是服務器的返回狀態碼(+OK或者-ERR) 114 public String getResult(String line){ 115 116 StringTokenizer st=new StringTokenizer(line," "); 117 118 return st.nextToken(); 119 } 120 121 //發送命令 122 private String sendServer(String str,BufferedReader in,BufferedWriter out) throws IOException{ 123 124 out.write(str);//發送命令 125 126 out.newLine();//發送空行 127 128 out.flush();//清空緩沖區 129 130 if(debug){ 131 132 System.out.println("已發送命令:"+str); 133 } 134 return getReturn(in); 135 } 136 137 //user命令 138 139 public void user(String user,BufferedReader in,BufferedWriter out) throws IOException{ 140 141 String result = null; 142 143 result=getResult(getReturn(in));//先檢測連接服務器是否已經成功 144 145 if(!"+OK".equals(result)){ 146 147 throw new IOException("連接服務器失敗!"); 148 } 149 150 result=getResult(sendServer("user "+user,in,out));//發送user命令 151 152 if(!"+OK".equals(result)){ 153 154 throw new IOException("用戶名錯誤!"); 155 } 156 } 157 158 //pass命令 159 public void pass(String password,BufferedReader in,BufferedWriter out) throws IOException{ 160 161 String result = null; 162 163 result = getResult(sendServer("pass "+password,in,out)); 164 165 if(!"+OK".equals(result)){ 166 167 throw new IOException("密碼錯誤!"); 168 } 169 } 170 171 172 //stat命令 173 174 public int stat(BufferedReader in,BufferedWriter out) throws IOException{ 175 176 String result = null; 177 178 String line = null; 179 180 int mailNum = 0; 181 182 line=sendServer("stat",in,out); 183 184 StringTokenizer st=new StringTokenizer(line," "); 185 186 result=st.nextToken(); 187 188 if(st.hasMoreTokens()) 189 190 mailNum=Integer.parseInt(st.nextToken()); 191 192 else{ 193 194 mailNum=0; 195 196 } 197 198 if(!"+OK".equals(result)){ 199 200 throw new IOException("查看郵箱狀態出錯!"); 201 } 202 203 System.out.println("共有郵件"+mailNum+"封"); 204 return mailNum; 205 } 206 207 //無參數list命令 208 public void list(BufferedReader in,BufferedWriter out) throws IOException{ 209 210 String message = ""; 211 212 String line = null; 213 214 line=sendServer("list",in,out); 215 216 while(!".".equalsIgnoreCase(line)){ 217 218 message=message+line+"\n"; 219 220 line=in.readLine().toString(); 221 } 222 223 System.out.println(message); 224 } 225 226 //帶參數list命令 227 public void list_one(int mailNumber ,BufferedReader in,BufferedWriter out) throws IOException{ 228 229 String result = null; 230 231 result = getResult(sendServer("list "+mailNumber,in,out)); 232 233 if(!"+OK".equals(result)){ 234 235 throw new IOException("list錯誤!"); 236 } 237 } 238 239 //得到郵件詳細信息 240 241 public String getMessagedetail(BufferedReader in) throws UnsupportedEncodingException{ 242 243 String message = ""; 244 245 String line = null; 246 247 try{ 248 line=in.readLine().toString(); 249 250 while(!".".equalsIgnoreCase(line)){ 251 252 message=message+line+"\n"; 253 254 line=in.readLine().toString(); 255 } 256 }catch(Exception e){ 257 258 e.printStackTrace(); 259 } 260 261 return message; 262 } 263 264 //retr命令 265 public void retr(int mailNum,BufferedReader in,BufferedWriter out) throws IOException, InterruptedException{ 266 267 String result = null; 268 269 result=getResult(sendServer("retr "+mailNum,in,out)); 270 271 if(!"+OK".equals(result)){ 272 273 throw new IOException("接收郵件出錯!"); 274 } 275 276 System.out.println("第"+mailNum+"封"); 277 System.out.println(getMessagedetail(in)); 278 Thread.sleep(3000); 279 } 280 281 //退出 282 public void quit(BufferedReader in,BufferedWriter out) throws IOException{ 283 284 String result; 285 286 result=getResult(sendServer("QUIT",in,out)); 287 288 if(!"+OK".equals(result)){ 289 290 throw new IOException("未能正確退出"); 291 } 292 } 293 294 }
總結:
這個項目其實非常簡單,關鍵要理解兩個方面,一是怎么利用socket編程連接到服務器,二是POP3協議命令的格式和返回值的格式。理解了這兩個方面,就好做了。首先建立連接,然后通過socket對象獲取輸入輸出流對象,在輸入輸出流對象上發送命令和接受返回值,接收到返回值之后自己本地處理這些返回值就行啦。
說到輸入輸出流,java的輸入輸出流真的比較多,有點不好記,但是每個輸出流的原理是一樣,而且有封裝的特性。接下來一定要狠狠的把它搞懂了!