Java Socket 编程
网络编程简介
网络编程:指在网络通信协议下,不同计算机上运行的程序,可以进行数据传输。
网络编程三要素:
- IP 地址:网络中设备的唯一标识。
- 端口号:设备上应用程序的唯一标识。(用两个字节表示的整数,它的取值范围是 065535。其中,01023 之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用 1024 以上的端口号。)
- 协议:它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。常见的协议有 UDP 协议和 TCP 协议。
更多网络知识请参考本博客的网络系列文章。
InetAddress:IP 地址类
InetAddress 类表示 Internet协议(IP)地址。
相关方法:
方法名 | 说明 |
---|---|
static InetAddress getByName(String host) | 确定主机名称的 IP 地址。主机名称可以是机器名称,也可以是 IP 地址 |
String getHostName() | 获取此 IP 地址的主机名 |
String getHostAddress() | 返回文本显示中的 IP 地址字符串 |
代码示例:
import java.net.InetAddress;
public class InetAddressDemo {
public static void main(String[] args) throws UnknownHostException {
// InetAddress address = InetAddress.getByName("juno");
InetAddress address = InetAddress.getByName("192.168.1.66");
//public String getHostName():获取此IP地址的主机名
String name = address.getHostName();
//public String getHostAddress():返回文本显示中的IP地址字符串
String ip = address.getHostAddress();
System.out.println("主机名:" + name);
System.out.println("IP地址:" + ip);
}
}
UDP 编程
发送端
构造方法:
方法名 | 说明 |
---|---|
DatagramSocket() | 创建数据报套接字并将其绑定到本机地址上的任何可用端口 |
DatagramPacket(byte[] buf, int len, InetAddress add, int port) | 创建数据包,发送长度为 len 的数据包到指定主机的指定端口 |
相关方法:
方法名 | 说明 |
---|---|
void send(DatagramPacket p) | 发送数据报包 |
void close() | 关闭数据报套接字 |
void receive(DatagramPacket p) | 从此套接字接受数据报包 |
发送数据的步骤:
- 创建发送端的 Socket 对象(DatagramSocket);
- 创建数据,并把数据打包;
- 调用 DatagramSocket 对象的方法发送数据;
- 关闭发送端。
代码示例:
public class SendDemo {
public static void main(String[] args) throws IOException {
// 1. 创建发送端的Socket对象(DatagramSocket)
// DatagramSocket() 构造数据报套接字并将其绑定到本地主机上的任何可用端口
DatagramSocket ds = new DatagramSocket();
// 2. 创建数据,并把数据打包
// DatagramPacket(byte[] buf, int length, InetAddress address, int port)
// 构造一个数据包,发送长度为 length的数据包到指定主机上的指定端口号。
byte[] bys = "hello,udp,我来了".getBytes();
DatagramPacket dp = new DatagramPacket(bys,bys.length,InetAddress.getByName("127.0.0.1"),10086);
// 3. 调用DatagramSocket对象的方法发送数据
//void send(DatagramPacket p) 从此套接字发送数据报包
ds.send(dp);
// 4. 关闭发送端
// void close() 关闭此数据报套接字
ds.close();
}
}
接收端
构造方法:
方法名 | 说明 |
---|---|
DatagramPacket(byte[] buf, int len) | 创建一个 DatagramPacket 对象用于接收长度为 len 的数据包 |
相关方法:
方法名 | 说明 |
---|---|
byte[] getData() | 返回数据缓冲区 |
int getLength() | 返回要发送的数据的长度或接收的数据的长度 |
接收数据的步骤:
- 创建接收端的 Socket 对象(DatagramSocket);
- 创建一个数据包,用于接收数据;
- 调用 DatagramSocket 对象的方法接收数据;
- 解析数据包,并把数据在控制台显示;
- 关闭接收端。
代码示例:
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
// 创建接收端的Socket对象(DatagramSocket)
DatagramSocket ds = new DatagramSocket(12345);
// 创建一个数据包,用于接收数据
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
// 调用DatagramSocket对象的方法接收数据
ds.receive(dp);
// 解析数据包,并把数据在控制台显示
System.out.println("数据是:" + new String(dp.getData(), 0, dp.getLength()));
}
}
}
TPC 编程
客户端
Java 为客户端提供了 Socket 类,为服务器端提供了 ServerSocket 类。
构造方法:
方法名 | 说明 |
---|---|
Socket(InetAddress address, int port) | 创建流套接字并将其连接到指定 IP 指定端口号 |
Socket(String host, int port) | 创建流套接字并将其连接到指定主机上的指定端口号 |
相关方法:
方法名 | 说明 |
---|---|
InputStream getInputStream() | 返回此套接字的输入流 |
OutputStream getOutputStream() | 返回此套接字的输出流 |
示例代码:
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 创建客户端的Socket对象(Socket)
// Socket(String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号
Socket s = new Socket("127.0.0.1", 10000);
// 获取输出流,写数据
// OutputStream getOutputStream() 返回此套接字的输出流
OutputStream os = s.getOutputStream();
os.write("hello,tcp,我来了".getBytes());
// 释放资源
s.close();
}
}
服务端
构造方法:
方法名 | 说明 |
---|---|
ServletSocket(int port) | 创建绑定到指定端口的服务器套接字 |
相关方法:
方法名 | 说明 |
---|---|
Socket accept() | 监听要连接到此的套接字并接受它 |
注意事项:
- accept 方法是阻塞的,作用就是等待客户端连接。
- read 方法也是阻塞的。
示例代码:
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 创建服务器端的Socket对象(ServerSocket)
// ServerSocket(int port):创建绑定到指定端口的服务器套接字
ServerSocket ss = new ServerSocket(10000);
// Socket accept():监听要连接到此套接字并接受它
Socket s = ss.accept();
// 获取输入流,读数据,并把数据显示在控制台
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len = is.read(bys);
String data = new String(bys,0,len);
System.out.println("数据是:" + data);
// 释放资源
s.close();
ss.close();
}
}
案例:1V1 聊天
案例需求:
-
客户端:发送数据,接受服务器反馈。
-
服务器:收到消息后给出反馈。
案例分析:
- 客户端创建对象,使用输出流输出数据。
- 服务端创建对象,使用输入流接受数据。
- 服务端使用输出流给出反馈数据。
- 客户端使用输入流接受反馈数据。
代码实现:
// 客户端
public class ClientDemo {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 10000);
OutputStream os = socket.getOutputStream();
os.write("hello".getBytes());
// os.close(); 如果在这里关流,会导致整个 socket 都无法使用
socket.shutdownOutput(); // 仅仅关闭输出流,并会写一个结束标记,对socket没有任何影响
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while((line = br.readLine())!=null){
System.out.println(line);
}
br.close();
os.close();
socket.close();
}
}
// 服务端
public class ServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10000);
Socket accept = ss.accept();
InputStream is = accept.getInputStream();
int b;
while((b = is.read())!=-1){
System.out.println((char) b);
}
System.out.println("看看我执行了吗?");
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
bw.write("你谁啊?");
bw.newLine();
bw.flush();
bw.close();
is.close();
accept.close();
ss.close();
}
}
案例:文件上传
案例需求:
-
客户端:数据来自于本地文件,接收服务器反馈。
-
服务器:接收到的数据写入本地文件,给出反馈。
案例分析:
- 创建客户端对象,创建输入流对象指向文件,每读一次数据就给服务器输出一次数据,输出结束后使用 shutdownOutput() 方法告知服务端传输结束。
- 创建服务器对象,创建输出流对象指向文件,每接受一次数据就使用输出流输出到文件中,传输结束后。使用输出流给客户端反馈信息。
- 客户端接受服务端的回馈信息。
相关方法:
方法名 | 说明 |
---|---|
void shutdownInput() | 将此套接字的输入流放置在“流的末尾” |
void shutdownOutput() | 禁止用此套接字的输出流 |
代码实现
// 客户端
public class ClientDemo {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1",10000);
// 本地流,用来读取本地文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("socketmodule\\ClientDir\\1.jpg"));
// 写到服务器 --- 网络中的流
OutputStream os = socket.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(os);
int b;
while((b = bis.read())!=-1){
bos.write(b); // 通过网络写到服务器中
}
bos.flush();
// 给服务器一个结束标记,告诉服务器文件已经传输完毕
socket.shutdownOutput();
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while((line = br.readLine()) !=null){
System.out.println(line);
}
bis.close();
socket.close();
}
}
// 服务端
public class ServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10000);
Socket accept = ss.accept();
// 网络流,从客户端读取数据
BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
// 本地流,把数据写到本地中,实现持久化存储
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("socketmodule\\ServerDir\\copy.jpg"));
int b;
while((b = bis.read()) !=-1){
bos.write(b);
}
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
bw.write("上传成功");
bw.newLine();
bw.flush();
bos.close();
accept.close();
ss.close();
}
}