目录
基于TCP和UDP协议的通信
基于TCP/IP协议的通信
- TCP/IP协议会在通信两端建立连接(虚拟链路),用于发送和接收数据;
- TCP/IP协议是一种可靠的网络协议,它通过重发机制来保证这种可靠性;
通信的实现
- Serversocket用于监听来自客户端的连接,当没有连接时它处于阻塞状态;
- 客户端使用Socket连接到指定的服务器。
基于UDP协议的通信
- UDP协议不会在通信两端建立连接(虚拟链路),而是直接发送数据;
- UDP协议是一种不可靠的网络协议,但是这种协议的通信效率非常高;
通信的实现
- Datagramsocket用于两端的通信,它不负责维护状态、不产生IO流,仅仅是发送或接收数据报;
- DatagramPacket代表数据报。
Java使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信
InetAddress
public static void main(String[] args) throws IOException {
InetAddress baidu = InetAddress.getByName("www.baidu.com"); //通过域名创建InetAddress对象
System.out.println(baidu.getHostAddress());//获取主机地址
System.out.println(baidu.isReachable(2000));//判断目标地址是否可达
InetAddress local = InetAddress.getByAddress(new byte[]{127, 0, 0, 1});//通过ip地址创建InetAddress对象
System.out.println(local.getHostName());
System.out.println(local.isReachable(2000));
}
基于TCP协议的通信,一次请求的对接
服务端程序
public class TcpServer {
public static void main(String[] args){
try{
//服务端的端口需要指定,但客户端的端口是随机分配的
ServerSocket serverSocket = new ServerSocket(9000);
//这里是个阻塞的逻辑,可以观察到当运行程序的时候,即使是死循环,控制台没有输出任何信息
while(true){
Socket socket = serverSocket.accept();//返回的是客户端访问的一些信息,如果没有用户就会阻塞在这里
System.out.println("请求:" + socket.toString());
//往客户端输出数据(站在服务端的角度)
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println("Welcome " + socket.getInetAddress().getHostAddress());
//通信结束需要关闭socket,而且内部的输入输出流也会自动关闭,不用我们手动关闭
socket.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
客户端程序
public class TcpClient {
public static void main(String[] args){
try{
Socket socket = new Socket("127.0.0.1", 9000);
//读取服务端给我们发送过来的数据(输入流)
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = br.readLine();
System.out.println(line);
socket.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
基于TCP协议的通信,实现一个聊天室
A给服务端发送一个消息,服务端再把这个消息发送给所有的用户,所以,服务端需要能够支持多个客户端并发访问(多线程)
客户端也需要并发(也要基于多线程),因为可以一边输入消息,一边看到别人给我们发过来的消息。
服务端程序
public class TcpServer {
public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
//记录每一个客户端(包装成线程安全的集合)
public static List<Socket> socketList = Collections.synchronizedList(new ArrayList<>());
public static void main(String[] args){
try{
ServerSocket serverSocket = new ServerSocket(9000);
while(true){
Socket socket = serverSocket.accept();
socketList.add(socket);
//每个人给我发过来的消息我都需要创建一个线程去完成处理,作为一个参数传递进去
threadPool.submit(new ThreadTask(socket));
}
}catch (Exception e){
e.printStackTrace();
}
}
static class ThreadTask implements Runnable{
private Socket socket;
private BufferedReader reader;//主要操作是从socket里面读到一条数据,所以需要使用到BufferedReader类
public ThreadTask(Socket socket) {
this.socket = socket;
try {
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void run() {
try{
String line = reader.readLine();
if(line != null){
for(Socket client : TcpServer.socketList){
String from = client.getInetAddress().getHostAddress() + ":" + socket.getPort();
String content = from + "说:" + line;//这是需要发送给每个客户端的内容
//注意:这里不能关闭流(都是长连接的,因为可以无限地交流下去)
//需要创建输出流,往输出流中写入内容
new PrintStream(client.getOutputStream()).println(content);
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
客户端程序
public class TcpClient {
public static ExecutorService threadPool = Executors.newFixedThreadPool(3);
public static void main(String[] args){
try{
//连上服务器以后一边需要从服务器读消息,一边需要向服务器写消息,需要两个线程
Socket socket = new Socket("127.0.0.1", 9000);
threadPool.submit(new ReadTask(socket));
threadPool.submit(new WriteTask(socket));
}catch (Exception e){
e.printStackTrace();
}
}
public static class ReadTask implements Runnable{
private Socket socket;
private BufferedReader reader;
public ReadTask(Socket socket) {
this.socket = socket;
try{
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}catch (Exception e){
e.printStackTrace();
}
}
//从服务器端读取任何人发送过来的消息
@Override
public void run() {
try{
String line;
while((line = reader.readLine()) != null){
System.out.println(line);
}
}
catch (Exception e){
e.printStackTrace();
}
}
}
public static class WriteTask implements Runnable{
private Socket socket;
private PrintStream writer;
public WriteTask(Socket socket) {
this.socket = socket;
try{
writer = new PrintStream(socket.getOutputStream());
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void run() {
Scanner scanner = new Scanner(System.in);
String line;
while((line = scanner.nextLine()) != null){
writer.println(line);
}
}
}
}
基于UDP协议的通信,简陋地模拟游戏关闭服务通知场景
底层不是通过数据流完成通信的,而是通过数据报完成数据通信的
服务端程序
public class UdpServer {
public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
public static List<InetSocketAddress> addressList = new ArrayList<>(); //只需要存储IP的端口,UDP是面向报文流的,只要能代表唯一的客户端即可
public static void main(String[] args) {
try{
//创建,随时准备接收
DatagramSocket socket = new DatagramSocket(9001);
//随时通知
threadPool.submit(new SendTask(socket));
//接受注册[接受数据,把数据放在buffer里面]
byte[] buffer = new byte[1024];
//创建数据报,接收的数据放到buffer里面
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while (true){//因为接收是不停地接收
//receive会把socket中的数据写到packet中
socket.receive(packet);//packet中就会有内容
addressList.add((InetSocketAddress) packet.getSocketAddress());
}
}catch (Exception e){
e.printStackTrace();
}
}
}
//创建一个超级管理员,可以随时发送通知
class SendTask implements Runnable{
private DatagramSocket socket;
public SendTask(DatagramSocket socket) {
this.socket = socket;
}
@Override
public void run() {
try{
//管理员敲击几个字发送给每个注册的用户
Scanner scanner = new Scanner(System.in);
String line;
while((line = scanner.nextLine()) != null){
for(InetSocketAddress isa : UdpServer.addressList){
byte[] buffer = line.getBytes();//把字符串的转换为字节数组即可
DatagramPacket packet = new DatagramPacket(
buffer, buffer.length, isa.getAddress(), isa.getPort());
socket.send(packet);
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
客户端程序
public class UdpClient {
public static ExecutorService threadPool = Executors.newFixedThreadPool(3);
public static void main(String[] args){
try{
DatagramSocket socket = new DatagramSocket();//client端口是随机的
//注册
DatagramPacket packet = new DatagramPacket(new byte[]{1}, 1, InetAddress.getByName("127.0.0.1"), 9001);
socket.send(packet);
//接收
threadPool.submit(new ReceiveTask(socket));
}catch (Exception e){
e.printStackTrace();
}
}
public static class ReceiveTask implements Runnable{
private DatagramSocket socket;
public ReceiveTask(DatagramSocket socket) {
this.socket = socket;
} //接收从服务端发送过来的数据
@Override
public void run() {
try{
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while(true){
socket.receive(packet);
//传递的是接收的有效值(也就是实际的长度)
String line = new String(packet.getData(),0, packet.getLength());
System.out.println(line);
}
}
catch (Exception e){
e.printStackTrace();
}
}
}
}