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的输入输出流真的比较多,有点不好记,但是每个输出流的原理是一样,而且有封装的特性。接下来一定要狠狠的把它搞懂了!