学习笔记09——Java基础(五)

本文详细介绍了Java中的网络编程,包括HTTP、TCP、UDP协议的特点与区别,重点讲解了TCP的三次握手和四次挥手。此外,还探讨了Socket编程,包括建立TCP连接的步骤,ServerSocket和Socket类的主要方法。接着,文章转向邮件发送,讲解了邮件服务器、SMTP和POP3协议,以及使用Java实现邮件发送的步骤和获取邮箱授权码的流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天整理一下Java网络编程与邮件发送。

一、网络编程

网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来。

java.net 包中 J2SE 的 API 包含有类和接口,它们提供低层次的通信细节,可以直接使用这些类和接口,来专注于解决问题,而不用关注通信细节。

(一)网络协议

常见的网络协议又HTTP,TCP,UDP

1、HTTP协议(HyperText Transfer Protocol,超文本传输协议)

(1)特点:

  • 一个应用层协议。
  • 支持客户/服务器模式。
  • 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
  • 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
  • 无连接:限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
  • 无状态:协议对于事务处理没有记忆,如果后续处理需要前面的信息,那么它必须重传,可能导致每次连接传送的数据量增大,但在服务器不需要先前信息时它的应答就较快。为此Web程序引入了Cookie机制来维护状态。

(2)Session与Cookie:

  • 严格来说,Session和Cookie并不是http协议的一部分,它们都是为了解决HTTP协议无状态的缺陷问题而提出来的解决方案。
  • Cookie机制采用在客户端保持状态的方案来解决THHP无状态的问题。
  • Session机制采用的是在服务器端保持状态的方案。由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的,但它也有其他选择。

2、TCP协议(Transmission Control Protocol,传输控制协议)

(1)特点:

  • 面向连接的、可靠的、基于字节流传输层通信协议。
  • 每一条TCP连接只能有两个端点,每一条TCP连接只能是点对点
  • TCP提供可靠交付的服务。
  • TCP提供全双工通信。数据在两个方向上独立的进行传输。因此,连接的每一端必须保持每个方向上的传输数据序号。
  • 面向字节流。面向字节流的含义:虽然应用程序和TCP交互是一次一个数据块,但TCP把应用程序交下来的数据仅仅是一连串的无结构的字节流。

(2)三种通信方式:

  • 单工:数据传输只支持数据在一个方向上传输,在同一时间只有一方能接受或发送信息,不能实现双向通信,如:电视,广播。
  • 半双工:数据传输允许数据在两个方向上传输,但在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;在同一时间只可以有一方接受或发送信息,可以实现双向通信。举例:对讲机。
  • 全双工:数据通信允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力;在同一时间可以同时接受和发送信息,实现双向通信,举例:电话通信。

(3)TCP协议中的三次握手与四次挥手:

三次握手

  • 第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
  • 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
  • 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

个人理解就是 :客户端要连服务端,它告诉服务端“我想连接你的服务,你同意一下呗”;服务端收到它的请求后又告诉客户端“我知道你要连我了,我同意了”;客户端收到服务后再告诉服务器一声“你放心,我收到服务了”,然后这两端都进入连接成功的状态。

四次挥手

  • 第一次挥手:客户端给服务端发送FIN报文,用来关闭客户端到服务端的数据传送。将标志位FIN和ACK置为1。
  • 第二次挥手:服务端收到FIN后,发回一个ACK(标志位ACK=1)。
  • 第三次挥手:当服务端确定数据已发送完成,则向客户端发送FIN报文,关闭与客户端的连接。
  • 第四次挥手:客户端收到服务器发送的FIN之后,发回ACK确认(标志位ACK=1)。

 个人理解就是:客户端这边不给服务器端发报文了,告诉它“我没有东西给你了,我要关闭了,但你要还有东西没发没发完我就等等你”;然后服务端收到客户端要关闭的通知后告诉客户端“我知道你要关闭了,但我还有数据没传完,你等等我啊”;等服务端传完后告诉客户端“我传完了,也要关闭连接了”;客户端收到通知后告诉客户端“我知道可以关了,但我想再确认一下”,等了一会后没收到服务端回应客户端知道“这兄弟竟然不回我,看了是真关闭了,那我也关了吧”,然后它也关闭了。

(4)为什么需要四次挥手?

因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

3、UDP(User Datagram Protocol,用户数据报协议)

(1)特点:

  • UDP是传输层的协议,功能即为在IP的数据报服务之上增加了最基本的服务:复用分用以及差错检测
  • UDP无连接,时间上不存在建立连接需要的时延。空间上,TCP需要在端系统中维护连接状态,需要一定的开销。此连接装入包括接收和发送缓存,拥塞控制参数和序号与确认号的参数。UCP不维护连接状态,也不跟踪这些参数,开销小。空间和时间上都具有优势。
  • UDP没有拥塞控制,应用层能够更好的控制要发送的数据和发送时间,网络中的拥塞控制也不会影响主机的发送速率。某些实时应用要求以稳定的速度发送,能容 忍一些数据的丢失,但是不能允许有较大的时延(比如实时视频,直播等)
  • UDP提供尽最大努力的交付,不保证可靠交付。
  • UDP是面向报文的,对应用层交下来的报文,添加首部后直接乡下交付为IP层,既不合并,也不拆分,保留这些报文的边界。对IP层交上来UDP用户数据报,在去除首部后就原封不动地交付给上层应用进程,报文不可分割,是UDP数据报处理的最小单位。
  • UDP常用一次性传输比较少量数据的网络应用

(2)TCP与UDP的区别

  1. TCP协议面向连接,UDP协议面向非连接;

  2. TCP协议传输速度慢,UDP协议传输速度快

  3. TCP有丢包重传机制,UDP没有;

  4. TCP协议保证数据正确性,UDP协议可能丢包;

(二)Socket编程

1、概述

套接字使用TCP提供了两台计算机之间的通信机制。 客户端程序创建一个套接字,并尝试连接服务器的套接字。

当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行通信。

2、两台计算机之间使用套接字建立TCP连接的步骤

  • 服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信。

  • 服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。

  • 服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接。

  • Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket 对象能够与服务器进行通信。

  • 在服务器端,accept() 方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。

3、ServerSocket 类的方法

(1)ServerSocket 四个构造方法:

序号方法描述
1public ServerSocket(int port) throws IOException
创建绑定到特定端口的服务器套接字。
2public ServerSocket(int port, int backlog) throws IOException
利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。
3public ServerSocket(int port, int backlog, InetAddress address) throws IOException
使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。
4public ServerSocket() throws IOException
创建非绑定服务器套接字。

(2)ServerSocket 常用方法:

序号方法描述
1public int getLocalPort()
  返回此套接字在其上侦听的端口。
2public Socket accept() throws IOException
侦听并接受到此套接字的连接。
3public void setSoTimeout(int timeout)
 通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位。
4public void bind(SocketAddress host, int backlog)
将 ServerSocket 绑定到特定地址(IP 地址和端口号)。

4、Socket 类的方法

(1)Socket的五个构造方法:

序号方法描述
1public Socket(String host, int port) throws UnknownHostException, IOException.
创建一个流套接字并将其连接到指定主机上的指定端口号。
2public Socket(InetAddress host, int port) throws IOException
创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
3public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException.
创建一个套接字并将其连接到指定远程主机上的指定远程端口。
4public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException.
创建一个套接字并将其连接到指定远程地址上的指定远程端口。
5public Socket()
通过系统默认类型的 SocketImpl 创建未连接套接字

(1)Socket的常用方法:

序号方法描述
1public void connect(SocketAddress host, int timeout) throws IOException
将此套接字连接到服务器,并指定一个超时值。
2public InetAddress getInetAddress()
 返回套接字连接的地址。
3public int getPort()
返回此套接字连接到的远程端口。
4public int getLocalPort()
返回此套接字绑定到的本地端口。
5public SocketAddress getRemoteSocketAddress()
返回此套接字连接的端点的地址,如果未连接则返回 null。
6public InputStream getInputStream() throws IOException
返回此套接字的输入流。
7public OutputStream getOutputStream() throws IOException
返回此套接字的输出流。
8public void close() throws IOException
关闭此套接字。

5、InetAddress 类的方法

序号方法描述
1static InetAddress getByAddress(byte[] addr)
在给定原始 IP 地址的情况下,返回 InetAddress 对象。
2static InetAddress getByAddress(String host, byte[] addr)
根据提供的主机名和 IP 地址创建 InetAddress。
3static InetAddress getByName(String host)
在给定主机名的情况下确定主机的 IP 地址。
4String getHostAddress() 
返回 IP 地址字符串(以文本表现形式)。
5String getHostName() 
 获取此 IP 地址的主机名。
6static InetAddress getLocalHost()
返回本地主机。
7String toString()
将此 IP 地址转换为 String。

6、实例

package Socket;

import java.io.*;
import java.net.*;

//服务端
public class GreetingServer{
    public static void main(String [] args)
    {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(8080);
            System.out.println("8080端口启动成功");
            while (true){
                Socket browseClient = serverSocket.accept();
                System.out.println("接受到"+browseClient.getInetAddress().getHostAddress()+"地址的请求");
                //新建一个线程去处理资源
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try{
                            //通过网络输入流获得用户请求信息
                            InputStream socketInputStream = browseClient.getInputStream();
                            //1-获得BufferedReader对象
                            BufferedReader br = new BufferedReader(new InputStreamReader(socketInputStream));
                            //2-使用readLine方法 读取一行
                            String str = br.readLine();
                            //3-使用空格拆分字符串
                            String[] strArr = str.split("[' ']+");
                            //4-获取数组中的第一个索引位置
                            String loadPath = strArr[1];
                            //5-截取字符串
                            loadPath = loadPath.substring(1);
                            System.out.println("访问的资源路径是:"+loadPath);
                            //6-使用文件输入流指向gy.jpg文件
                            FileInputStream fis = new FileInputStream(new File("D:/java/FSR/src/Socket/file",loadPath));
                            //7-使用网络输出流 写回文件信息
                            OutputStream socketOutputStream = browseClient.getOutputStream();
                            //先把响应头信息写进去 http协议的响应头
                            socketOutputStream.write("HTTP/1.1 200 OK \r\n".getBytes());
                            socketOutputStream.write("\r\n".getBytes());
                            //再写访问资源信息
                            byte[] bs = new byte[1024];
                            int len = -1;
                            while ((len=fis.read(bs))!=-1) {
                                socketOutputStream.write(bs,0,len);
                            }
                            //8-关闭网络输出流
                            browseClient.shutdownOutput();
                            //9-关闭文件输入流
                            fis.close();
                            //10-关闭socket对象
                            browseClient.close();
                        } catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


//启动后,浏览器上访问http://localhost:8080/(D:/java/FSR/src/Socket/file目录下的任意文件)

输出结果:
8080端口启动成功
接受到0:0:0:0:0:0:0:1地址的请求
接受到0:0:0:0:0:0:0:1地址的请求
访问的资源路径是:gy.JPG
访问的资源路径是:favicon.ico
java.io.FileNotFoundException: D:\java\FSR\src\Socket\file\favicon.ico (系统找不到指定的文件。)
	at java.io.FileInputStream.open0(Native Method)
	at java.io.FileInputStream.open(FileInputStream.java:195)
	at java.io.FileInputStream.<init>(FileInputStream.java:138)
	at Socket.GreetingServer$1.run(GreetingServer.java:36)
	at java.lang.Thread.run(Thread.java:745)

说明:
这个问题可忽略,因为favicon.ico是它访问的网络图标

二、邮件发送

(一)邮件服务器

要在网络上实现邮件功能,必须要有专门的邮件服务器。这些邮件服务器类似于现实生活中的邮局,它主要负责接收用户投递过来的邮件,并把邮件投递到邮件接收者的电子邮箱中。

SMTP服务器地址:一般是 smtp.xxx.com,比如163邮箱是smtp.163.com,qq邮箱是smtp.qq.com。

(二)传输协议

1、SMTP协议

通常把处理用户smtp请求(邮件发送请求)的服务器称之为SMTP服务器(邮件发送服务器)。

2、POP3协议

通常把处理用户pop3请求(邮件接收请求)的服务器称之为POP3服务器(邮件接收服务器)。

(三)邮件发送实现步骤

  • 下载一个mail.jar
  • 导包
  • 创建一个用于存放配置信息的对象,Properties类型
  • 设置发送邮件需要的一些信息
  • 创建一个session对象
  • 通过session对象获取一个Transport对象
  • 邮件服务器认真,获得一个认证码
  • 创建映射关系
  • 发送邮件
  • 关闭通道

(四)获取邮箱授权码(以QQ邮箱为例)

1、登录QQ,进入设置

 

2、进入账户

3、下拉到这

4、点击开启服务,按步骤来

(五)示例

package test;

import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;

public class TestSendEmail {

    //设计一个方法,创建一个邮件对象
    //参数?  一个Session对象
    //返回值? 一个邮件(映射) MimeMessage
    private static Message createMessage(Session session) throws MessagingException {
        //自己创建一个邮件对象
        Message message = new MimeMessage(session);
        /*设置邮件的基本信息*/
        //设置发送人
        message.setFrom(new InternetAddress("2669168424@qq.com"));
        //设置接收人 recipient容器收纳  参数:类型(收件人To,抄送人CC,密送/暗送BCC)
        message.setRecipient(Message.RecipientType.TO,new InternetAddress("2015591866@qq.com"));
        /*//设置发送邮件的时间
        message.setSentDate(new Date());*/
        message.setSubject("测试");
        //设置发送邮件正文
        message.setText("这是一个小测试");
        //以上所有信息要保存才能生效,因为这个message只是一个映射对象,并不真正存在,就像文件流一样,需要flush一下
        message.saveChanges();
        //将message对象返回
        return message;
    }

    public static void main(String[] args) throws MessagingException {
        //1、下载一个mail.jar
        //2、导包
        //3、创建一个用于存放配置信息的对象,Properties类型
        Properties prop = new Properties();
        //4、设置发送邮件需要的一些信息
        //设置发送邮件的协议  smtp
        prop.put("mail.transport.protocol","smtp"); //必须
        //设置发送邮件的主机名
        prop.put("mail.smtp.host","smtp.qq.com"); //必须
        //设置发送邮件的端口,默认25,还有110,143,465
        //prop.put("mail.smtp.port","xxx");
        //设置发送邮件时,是否需要进行身份验证
        //prop.put("mail.smtp.auth","true");
        //设置是否使用ssl安全连接,默认使用
        //prop.put("mail.smtp.ssl.enable","true");
        //5、创建一个session对象(可以理解为是一个Socket,Java和邮箱之间建立一个连接)
        Session session = Session.getDefaultInstance(prop);
        //6、通过session对象获取一个Transport对象(可以理解为是一个输出流)
        Transport ts = session.getTransport();
        //7、想要通过邮箱发送邮件,必须得到邮件服务器的认证
        ts.connect("2669168424@qq.com","zritukpvdouleafa");
        //8、发送的是一封邮件(创建一个邮件的映射关系) File对象 Class对象 映射
        //  发送邮件对象本身比较复杂,需要好多信息,单独写一个方法
        Message message = createMessage(session);
        //9、发送邮件(需要一个message对象,知道message对象中的哪些接收人)
        ts.sendMessage(message,message.getAllRecipients());
        //10、关闭通道
        ts.close();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值