一.Socket概述
Java网络编程主要涉及的内容是Socket编程。
Socket(套接字),是网络上两个程序之间实现数据交换的一端,它既可以发送请求,也可以接受请求,一个Socket由一个IP地址和一个端口号唯一确定,利用Socket能比较方便的实现两端(服务端和客户端)的网络通信。
在Java中,有专门的Socket类来处理用户请求和响应,学习使用Socket类方法,就可以实现两台机器之间通信。
Socket通信是有两种方式的:TCP和UDP。
TCP通信:客户端提供了java.net.Socket
类,服务器端提供了java.net.ServerSocket
类。
UDP通信:UDP通信不建立逻辑连接,使用DatagramPacket
类打包数据包,使用DatagramSocket
类发送数据包。
Socket通信模型如下图:
二. TCP通信客户端Socket
Java中专门用来实现Socket客户端的类就叫Socket
,这个类实现了客户端套接字,用于向服务器发出连接请求等。
- 构造方法:
Socket(String host, int port)
:创建一个流套接字并将其连接到指定IP地址的指定端口号。
如果host为null,则相当于指定地址为回送地址。
回送地址(来自百度百科):
127.x.x.x是本机的会送地址,即主机IP堆栈内部的IP地址,主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,协议软件立即返回之,不进行任何网络传输。
- 主要方法:
InputStream getInputStream()
:返回此套接字的输入流。
关闭生成的InputStream也将关闭相关的Socket。OutputStream getOutputStream()
:返回此套接字的输出流。
关闭生成的OutputStream也将关闭相关的Socket。void close()
:关闭此套接字
三. TCP通信服务器端ServerSocket
Java中专门用来建立Socket服务器的类叫ServerSocket
,这个类实现了服务器套接字,该对象等待通过网络的请求。
- 构造方法:
ServerSocket(int port)
:创建绑定到特定端口的服务器套接字。 - 主要方法:
Socket accept()
:监听并接受连接,返回一个新的Socket对象,用于和客户端通信,该方法会一直阻塞直到建立连接。void close()
:关闭此套接字。
四.基于TCP的Socket通信
- 步骤分析:
- 服务端先启动,创建ServerSocket对象,等待连接。
- 客户端启动,创建Socket对象,请求连接。
- 服务器端接收请求,调用accept方法,并返回一个Socket对象。
- 客户端的Socket对象获取OutputStream,向服务器端发送数据。
- 服务器端Socket对象获取InputStream,读取客户端的数据。
- 服务器端Socket对象获取OutputStream,向客户端发送数据。
- 客户端的Socket对象获取InputStream,读取服务器的数据。
- 客户端释放资源,断开连接。
- 主要代码
- 服务器端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) throws IOException {
System.out.println("Server start...");
ServerSocket server = new ServerSocket(1234);
while (true) {
Socket client = server.accept();
// 字节流转为字符流
InputStreamReader isr = new InputStreamReader(client.getInputStream());
// 字符流转换为缓冲流
BufferedReader br = new BufferedReader(isr);
// 读取
String line = null;
while ((line = br.readLine()) != null) {
System.out.println("接收到客户端信息:" + line);
}
// 发送信息到客户端 字节流转字符流
OutputStreamWriter out = new OutputStreamWriter(client.getOutputStream());
// 字符流转换为缓冲流
BufferedWriter bw = new BufferedWriter(out);
String str = "你好,服务器收到了";
bw.write(str);
bw.flush();
// 关闭资源
client.close();
}
}
}
- 客户端
import java.io.*;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args) throws IOException {
System.out.println("Client start...");
Socket client = new Socket("localhost", 1234);
OutputStreamWriter osw = new OutputStreamWriter(client.getOutputStream());
BufferedWriter bw = new BufferedWriter(osw);
String str = "你好,我是客户端";
bw.write(str);
bw.flush();
// 发送一个终结符,告诉服务器,已经发送完毕
client.shutdownOutput();
InputStreamReader isr = new InputStreamReader(client.getInputStream());
BufferedReader br = new BufferedReader(isr);
String line = null;
while((line = br.readLine()) != null) {
System.out.println("接收到服务器消息:" + line);
}
// 释放资源
client.close();
}
}
注意:
在交互时,服务器accept是阻塞的,read也是阻塞的,所以在发送完数据后,需要调用shutdownOutput来告知发送结束,否则会一直阻塞。
五.UDP相关类DatagramPacket类和DatagramSocket类
- 数据包类DatagramPacket
- 作用:用来封装发送端或接收端要发送或接收的数据。
- 构造方法
-
DatagramPacket(byte[] buf, int length)
:构造DatagramPacket,用来接收长度为length的数据包。 -
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
:构造数据报包,用来将长度为length的包发送到指定主机上的指定端口号。
- 常用方法
-
public int getLength()
:获得发送端实际发送的字节数或接收端世界接收的字节数 -
public int getPort()
:获得发送端或接收端端口号
- 发送数据包类DatagramSocket
- 作用:用来发送和接收数据包对象
- 构造方法
-
DatagramSocket()
:构造数据报套接字并将其绑定到本地主机上任何可用的端口。 -
DatagramSocket(int port)
:创建数据包套接字并将其绑定到本地主机上指定端口。
- 常用方法
-
public void send(DatagramPacket p)
:从此套接字发送数据报包 -
public void receive(DatagramPacket p)
:从此套接字接收数据报包 -
public void close()
:关闭此数据报套接字
- InetAddress类(无构造方法)
- 作用:代表一个IP地址
- 静态方法
-
public static InetAddress getLocalHost()
:返回本地主机 -
public static InetAddress getByName()
:在给定主机名的情况下确定主机的 IP 地址。
- 普通方法
-
public String getHostName()
: 获取此 IP 地址的主机名。 -
public String getHostAddress()
:返回 IP 地址字符串(以文本表现形式)
六.基于UDP的Socket通信
- 步骤分析
- 服务器端先启动,创建DatagramSocket对象,监听端口,用于接收
- 服务器端创建DatagramPacket对象,打包用于接收的数据包
- 服务器阻塞等待接收
- 客户端启动,创建DatagramSocket对象,监听端口,用于接收
- 客户端创建DatagramPacket对象,打包用于发送的数据包
- 客户端发送数据,服务端接收
- 服务端接收数据后,创建DatagramPacket对象,打包用于发送的数据包,发送数据
- 客户端创建DatagramPacket对象,打包用于接收的数据包,阻塞等待接收
- 客户端接收服务端数据,断开连接,释放资源
- 主要代码
- 服务器
import java.io.IOException;
import java.net.*;
public class UDPServer {
public static void main(String[] args) throws IOException {
System.out.println("Server Start...");
// 保存接收的数据
byte[] rData = new byte[1024];
// 接收时监听端口8888
DatagramSocket ds = new DatagramSocket(8888);
DatagramPacket rdp = new DatagramPacket(rData, rData.length);
while (true) {
ds.receive(rdp);
System.out.println("Receive:" + new String(rData));
// 发送数据
byte[] sData = "你好,我是服务器".getBytes();
DatagramPacket sdp = new DatagramPacket(sData, sData.length, rdp.getAddress(), rdp.getPort());
ds.send(sdp);
}
}
}
- 客户端
import java.io.IOException;
import java.net.*;
public class UDPClient {
public static void main(String[] args) throws IOException {
System.out.println("Client login...");
// 构建数据包
byte[] sendData = "Hello,I am Client".getBytes();
InetAddress sendAddress = InetAddress.getLocalHost();
DatagramPacket dp = new DatagramPacket(sendData, sendData.length, sendAddress, 8888);
DatagramSocket ds = new DatagramSocket(1234);
ds.send(dp);
// 创建数据包,接收数据
byte[] receiveData = new byte[1024];
DatagramPacket rdp = new DatagramPacket(receiveData, receiveData.length);
ds.receive(rdp);
System.out.println("Receive:" + new String(receiveData));
ds.close();
}
}
注意:
UDP通信双方,对应端口号一致才可以,比如发送方使用1234端口,接收方需要监听1234端口才可以。