网络编程--5
TCP协议相关类
InetSocketAddress类
Socket类
ServerSocket类
TCP客户端和服务器端
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
Socket通信端点具备了,但是数据传输协议是不一样的。因此每一种数据传输协议都有自己特有创建Socket通信端点的方法。TCP协议有自己创建Socket通信端点的类和方法
1. UDP协议相关类
UDP协议相关类的综述
(1). TCP和UDP创建的Socket通信端点的类型
[1]. UDP协议是面相无连接的协议。创建的数据传输的两个Socket通信端点用的是同一个DatagramSocket类进行创建的。因此UDP通信不区分通信双方的主次
[2]. TCP协议是面相连接的协议。创建的数据传输的两个Socket通信端点用的并不是同一个Socket类。通信的一方程序称为服务器Socket端点,使用ServerSocket类进行创建。通信的另一方程序称为客户端Socket端点,使用Socket类进行创建。
(2). TCP和UDP传输数据的类型
[1]. UDP协议是数据的传输形式数据报包。数据byte[]被DatagramPacket类封装成数据报包在通信端点之间进行传输。
【注意】Datagram Socket:数据报Socket -----因为UDP的数据通过数据报DatagramPacket传输的,因此UDP中的Socket端点称为数据报Socket(Datagram Socket)
[2]. TCP协议是数据的传输形式是IO网络流。没有特殊的类对数据进行封装,直接使用IO网络流对通行端点的数据进行传输。
【注意】Stream Socket:网络流Socket -----因为TCP的数据通过网络流传输的,因此TCP中的Socket端点称为网络流Socket (Stream Socket)
通过DatagramSocket创建的Socket端点称为数据报Socket
【结论】TCP通信中相关的类就是ServerSocket类和Socket类
2. java.net.InetSocketAddress类
1). SocketAddress抽象类
SocketAddress抽象类
[1]. SocketAddress抽象类所在的位置
SocketAddress位于java.net包中
[3]. SocketAddress类的直接父类
SocketAddress类的直接父类是java.lang.Object类
[4]. SocketAddress类的源码声明
public abstract class SocketAddress implements java.io.Serializable {}
【
注意】SocketAddress类中
没有任何方法
[5]. SocketAddress类的含义
对主机的IP和需要进行网络通信的App所对应的端口的封装。因此SocketAddress就可以理解为IP和port的封装。
2). InetSocketAddress类(简介)
[1]. InetSocketAddress抽象类所在的位置
InetSocketAddress位于java.net包中
[3]. InetSocketAddress类的直接父类
InetSocketAddress类的直接父类是java.net.SocketAddress抽象类
[4]. InetSocketAddress类的含义
这个类实现了一个含有IP地址+Port的SocketAddress。注意这个IP地址可以通过String类型的主机名hostname被解析成IP地址。因此InetAddress对主机的IP和需要进行网络通信的App所对应的端口的封装。因此Inet SocketAddress同样可以理解为IP和port的封装。
3. java.net.Socket类
1). Socket类基础知识
(1). Socket类
[1]. Socket类所在的位置
Socket位于java.net包中
[3]. Socket类的直接父类
Socket类的直接父类是java.lang.Object类
[4]. Socket类的源码声明
public class Socket {
//...
}
[5].
Socket类的
含义
两台主机间通信的端点(称客户端Socket端点或者Socket端点),实现了客户端的套接字。
(2). Socket类的构造方法
[1]. Socket构造方法中参数推断
{1}. 由于TCP通信在Java中的数据直接通过网络IO流进行传输。IO中的数据没有办法对传输的目的主机的IP和端口进行封装。
{2}. 通信端点的双方可以互相通信(收和发),所以必须在Socket的构造方法中指定创建的Socket的时候,就务必要指定这个Socket要绑定的主机(IP+ Port)和要通信的目标主机(IP+Port)
【Socket要绑定的主机】指的是通信端点Socket所在的App所在的主机
【注意】由于TCP数据传输协议是面向连接的,所以网络流Socket在实例化的时候一定要指定目标主机的SocketAddress (面向连接的体现)。
[1]. Socket类中的核心private构造函数
{1}. 构造方法原型
private Socket(SocketAddress address, SocketAddresslocalAddr, boolean stream) throws IOException;
{2}. 构造方法的参数
{2}1. SocketAddressaddress: 指定通信的目标主机App的SocketAddress
{2}2. SocketAddresslocalAddr: 绑定Socket所在的主机App的SocketAddress
{2}3. boolean stream:
true ----创建网络流Socket(StreamSocket)
false ----创建数据报Socket(DatagramSocket)
[2]. String 主机名,int端口号
{1}. 构造函数原型
public Socket(String host, int port)
{2}. 构造方法的参数
{2}1. String host: 通过字符串形式指定通信的目标主机的IP
{2}2. int port: 目标主机的App对应的端口号
{3}. 和private构造方法的关系
public Socket(String host, int port) throws UnknownHostException, IOException{
this(
//传入目标主机的SocketAddress
host != null ? new InetSocketAddress(host, port) : new InetSocketAddress(InetAddress.getByName(null), port),
//传入Socket被绑定的主机的SocketAddress ----传入null 系统自行解析本机
//的SocketAddress
(SocketAddress) null,
//创建网络流Socket
true
);
}
[2]. InetAddress 主机地址,int端口号
{1}. 构造函数原型
public Socket(InetAddress address, int port)
{2}. 构造方法的参数
{2}1. InetAddress address:指定通信的目标主机的IP
{2}2. int port: 目标主机的App对应的端口号
{3}. 和private构造方法的关系
public Socket(String host, int port) throws UnknownHostException, IOException{
this(
//传入目标主机的SocketAddress
address != null ? new InetSocketAddress(address, port) : null,
//传入Socket被绑定的主机的SocketAddress ----传入null 系统自行解析本机
//的SocketAddress
(SocketAddress) null,
//创建网络流Socket
true
);
}
[3]. String 主机名,int端口号 || String 主机名,int端口号
public Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException;
{2}. 构造方法的参数
{2}1. host + port:指定通信的目标主机的IP +目标主机App对应的端口号
{2}2.: localAddr + localPort: 指定本机的SocketAddress
{3}. 和private构造方法的关系
public Socket(String host, int port) throws UnknownHostException, IOException{
this(
//传入目标主机的SocketAddress
host != null ? new InetSocketAddress(host, port) : new InetSocketAddress(InetAddress.getByName(null), port),
//传入Socket被绑定的主机的SocketAddress
new InetSocketAddress(localAddr,localPort),
//创建网络流Socket
true
);
}
[4]. InetAddress 主机IP,int端口号 ||InetAddress 主机名,int端口号
{1}. 构造函数原型
public Socket(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOException;
{2}. 构造方法的参数
{2}1. address + port:指定通信的目标主机的IP +目标主机App对应的端口号
{2}2.: localAddr + localPort: 指定本机的SocketAddress
{3}. 和private构造方法的关系
public Socket(String host, int port) throws UnknownHostException, IOException{
this(
//传入目标主机的SocketAddress
address != null ? new InetSocketAddress(address, port) : null,
//传入Socket被绑定的主机的SocketAddress
new InetSocketAddress(localAddr,localPort),
//创建网络流Socket
true
);
}
[5]. 空参构造函数
{1}. 构造函数原型
public Socket() throws IOException;
{2}. 使用 注意事项
一旦初始化完毕,就要立刻用过相应的方法去连接目标SocketAddress。通过方法void connect(SocketAddress endPoint)来连接目标主机的SocketAddress
2). Socket类负责通信的方法
由于TCP传输通过IO网络流进行传输,没有名字叫做send和receive的方法。通过输入流和输出流的方式进行数据传输。
(1). 获取网络输入流 ----用于通信双方的“接收”
[1]. 方法功能描述
返回这个客户端Socket通信端点的网络输入流
[2]. 方法原型
public InputStream getInputStream() throws IOException;
[3]. 获取Socket 输入流的 作用
可以通过这个输入流从Socket读取字节数据byte[ ]
(2). 获取网络输出流 ----用于通信双方的“发送”
[1]. 方法功能描述
返回这个客户端Socket通信端点的网络输入流
[2]. 方法原型
public InputStream getInputStream() throws IOException;
[3]. 获取Socket 输出流的 作用
可以通过这个输出流向Socket写入字节数据byte[ ]
【总结】如果这个Socket和服务端的Socket有一个相关联的数据传输通道,那么通过返回的输入流和输出流就可以进行各种对这个数据传输通道和Socket相关联的各种操作。
(3). 关闭客户端的Socket输入流
[1]. 方法功能描述
为该客户端Socket将输入流的状态设置为“流的末端”。这样操作之后就不可以从这个输入流读取数据。
[2]. 方法原型
public voidshutdownInput() throws IOException;
(4). 关闭客户端的Socket输出流
[1]. 方法功能描述
为这个Socket禁止掉输出流。调用这个方法之前写入输出流的数据都会被正常发送到目标地址。在调用这个关闭输出流之后,任何对这个流的写操作都会引起IOException。
[2]. 方法原型
public void shutdownOutput()throws IOException;
3). Socket类获取常规信息方法
(1). 获取本机的IP地址和进行网络通信的端口号
[1]. 获取本机的IP地址
public InetAddress getLocalAddress();
[2]. 获取 本机的进行网络通讯的App的端口号
public int getLocalPort();
[3]. 获取 本机的进行网络通讯的App的Socket地址
public SocketAddress getLocalSocketAddress();
(2). 获取目标通信本机的IP地址和进行网络通信的端口号
[1]. 获取目标通信主机的IP地址
public InetAddress getInetAddress();
[2]. 获取 目标通信主机的进行网络通讯的App的端口号
public int getPort();
[3]. 获取 目标通信主机的进行网络通讯的App的Socket地址
public SocketAddress getRemoteSocketAddress();
4. java.net.ServerSocket类
1). ServerSocket类基础知识
(1). ServerSocket类
[1]. ServerSocket类所在的位置
ServerSocket位于java.net包中
[3]. ServerSocket类的直接父类
ServerSocket类的直接父类是java.lang.Object类
[4]. ServerSocket类的源码声明
public class ServerSocket {
//...
}
[5].
ServerSocket类的
含义
这个类实现了服务器端的Socket通信端点。这个服务器端的Socket端点等待来自网络中的其他客户端Socket端点的请求。
(2). ServerSocket类的构造方法
[1]. ServerSocket构造方法中参数推断
由于ServerSocket可以通过自身的accept方法获取对该服务端Socket通信端点访问的其他Socket端点。获取的Socket类的端点可以获取到自身所在主机和目标主机的各种信息,因此ServerSocket的构造方法仅仅给出要监听的本机的App的端口就可以了。
[1]. int port
{1}. 构造方法原型
public ServerSocket(int port) throws IOException;
{2}. 构造方法的参数
int port: ServerSocket要监听的本机的App的端口号。
[2. 空参构造函数
{1}. 构造函数原型
public ServerSocket() throws IOException;
{2}. 使用 注意事项
一旦初始化完毕,就要立刻用过相应的方法去连接目标SocketAddress。通过方法void bind(SocketAddress endPoint)来连接目标主机的SocketAddress
2). Socket类负责通信的方法
获取向该服务器Socket发起请求的客户端Socket
[1]. 方法功能描述
监听要连接到这个ServerSocket服务端的连接并接受这个连接(这个TCP连接通道就被建立)。连接完成之后返回和这个服务端Socket建立连接的客户端Socket副本。
[2]. 方法原型
public Socket accept() throws IOException;
[3]. accept()方法是一个 阻塞式方法
这个方法会一直阻塞直到有来自客户端的Socket和这个ServerSocket之间的连接被创建。
3). ServerSocket类获取常规信息方法
获取本机的IP地址和进行网络通信的端口号
[1]. 获取ServerSocket所在本机的IP地址
public InetAddress getInetAddress();
[2]. 获取ServerSocket 所监听的本机的进行网络通讯的App的端口号
public int getLocalPort();
[3]. 获取ServerSocket被绑定的 本机的进行网络通讯的App的Socket地址
public SocketAddress getLocalSocketAddress();
5. TCP接收端和发送端
1). TCP客户端 ----Socket充当
(1). TCP客户端的操作流程 ----这里仅仅发送数据
[1]. 创建TCPSocket服务,指定目的主机和端口。
[2]. 为了发送数据,通过getInputStream()方法获取客户端Socket中的输出流。
[3]. 写数据到输出流, 并随网络发送到对应主机。
[4]. 关闭Socket资源 对应的流资源不用关闭。这是因为Socket资源被释放之后,通过Socket本身获得的输出流也随着Socket资源的释放而释放。
(2). TCP客户端发送数据代码示例
/*需求:给服务器端发送一个文本数据*/
import java.net.*;
import java.io.*;
class TcpClient{
public static void main(String[] args) throws Exception{
//1. 创建客户端Socket服务,指定目的主机和端口
Sockets =new Socket("127.0.0.1", 10003);
//2.为了发送数据,应该获取Socket中的输出流
OutputStreamout =s.getOutputStream();
//3.写数据到输出流,并随网络发送到对应主机
out.write("TCP is coming....".getBytes());
//4.关闭Socket资源对应的流资源不用关闭
s.close();
}
}
2). TCP服务器端 ----ServerSocket充当
(1). TCP接收端的操作流程 ----仅仅接收数据
[1]. 创建TCPServerSocket服务,指定要监听的端口。
[2]. 通过ServerSocket对象的accept()获取来自客户端的Socket对象。
[3]. 通过获取到的客户端Socket对象的getInputStream()方法读取客户端发送的信息。
[4]. 关闭ServerSocket资源【可选操作,一般情况下服务器端不会被关闭】
(2). TCP服务器端接收数据代码示例
/*需求:定义端接收数据,并打印在控制台上*/
class TcpServer{
public static void main(String[] args) throws Exception{
//1.建立服务器端Socket服务,并监听一个端口
ServerSocketss =new ServerSocket(10003);
//2.通过accept方法来获取连接过来的客户端对象 Server端本身没有流
//必须使用客户端对象的流对象与客户端本身进行通讯
Sockets =ss.accept();
InputStreamin =s.getInputStream();
//获取客户端的IP
Stringip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....connected!");
byte[] buf =new byte[1024];
int len =in.read(buf);
System.out.println(new String(buf, 0, len));
//客户端一定要关闭客户端的Socket对象
s.close();
ss.close();//服务器端对象的关闭操作是可选的
}
}
3). 演示结果
(1). 错误的启动顺序
[1]. 由于TCP传输协议是面向连接的。所以不能随意启动某个Socket通信端。
[2]. 先启动客户端Socket再启动服务端ServerSocket ------错误的启动顺序
此时客户端就会立刻去连接服务端。但是由于服务端的Socket没有被启动,所以面相连接的TCP数据传输通道就不能形成。此时会抛出java.net.ConnectionException异常,TCP数据传输失败。
(2). 正确的启动顺序
[1]. 先启动服务端ServerSocket再启动客户端Socket ------正确的启动顺序
【可行性分析】服务端的程序运行到accept()方法之后,没有发现有connection接入。此时服务端的程序被阻塞,暂停在那里等待。之后,客户端程序被启动并立刻去连接目标服务端。这时候发现服务端已经启动,就会建立传输通道。这时候,客户端和服务端就可以进行通信了。因此这个启动顺序可行。
[2]. TCP启动顺序和UDP启动顺序对比
{1}. TCP的启动顺序务必是先启动服务端,再启动客户端。否则,程序直接抛出异常
{2}. UDP的启动顺序是先启动接收端再启动客户端。但是不这么做,接收端无法接收到数据,但是程序不会发生异常。
(3). 运行结果
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------