day-3-2-2

发布于:2022-12-19 ⋅ 阅读:(329) ⋅ 点赞:(0)

Java网络编程

计算机网络是通过传输介质、通信设施和网络通信协议,把分散在不同地点的计算机设备互连起来的,实现资源共享和数据传输的系统。网络编程就是编写程序使互联网的两个或多个设备(如计算机)之间进行数据传输。Java语言对网络编程提供了良好的支持。通过其提供的接口我们可以很方便地进行网络编程。

IP地址相关操作

InetAddress是Java对IP地址的封装。其下有两个子类Inet4Address和Inet6Address。这个类的实例经常和UDP DatagramSockets和Socket,ServerSocket类一起使用

InetAddress 没有公开的构造方法,因此你必须通过一系列静态方法中的某一个来获取它的实例。

//www.baidu.com实际上是主机名称
//IPv4采用的是点分十进制的记法,所以难以记忆,
//引入DNS可以将一个有含义的字符串和一个IP地址建立对应关系,当使用有含义的字符串时可以通过DNS转换为IP地址
//InetAddress对象中可以封装一个IP地址和一个主机名
InetAddress ia = InetAddress.getByName("www.baidu.com");  //获取对应主机名称的IP地址,会自动调用DNS服务
		System.out.println(ia);
		System.out.println();
		InetAddress[] arr=InetAddress.getAllByName("www.baidu.com"); //获取对应主机名称的所有注册IP地址
		for(InetAddress tmp:arr)
			System.out.println(tmp);
		System.out.println();
		
//		new InetAddress()  没有提供可以直接使用的构造器,所以不能直接new创建对象
ia=InetAddress.getLocalHost();//获取本地机的IP地址
System.out.println(ia);  //lover/172.30.1.101
		
ia=InetAddress.getLoopbackAddress(); //获取回绕地址
System.out.println(ia);  //localhost/127.0.0.1
System.out.println(ia.getClass());  //class java.net.Inet4Address
System.out.println();
		
//通过IP地址查询对应信息
InetAddress ia1=InetAddress.getByName("14.215.177.39");
System.out.println(ia1);
//用于判断是否可以连接ia对象对应的主机,可以理解为尝试是否能够ping通
System.out.println(ia1.isReachable(2000));

URL访问网上资源

URL对象代表统一资源定位器,是指向互联网资源的指针,资源可以是简单的文件或目录,也可以是对复杂对象的引用,例如对数据库或搜索引擎的查询。用协议名、主机、端口和资源组成,即满足格式:protocol://host:port/resourceName,例如http://www.yan.com:80/index.php

http协议的标准端口为80 https=http+SSL 加密数据传输的http,标准端口为443

  • URL统一资源指针,可以指定一个具体的资源,例如一个html网页
  • URI统一资源标识符,可以认为为了方便记忆,给URL起的别名

构建URL对象

// 创建一个URL对象,用于指代网络中的一个资源,如果网址不合法,则抛出MalformedURLException

URL url = new URL(“https://news.cctv.com/2022/09/12/ARTIZ8cD6tXepRSQ5cFIHyA5220912.shtml”);

最重要的方法:

  • url.openConnection():URLConnection 可以获取输入、输出流
  • url.openStream():InputStream 直接获取输入流

不重要的方法:

通过URL对象的一些方法可以访问该URL对应的资源:

  • String getFile():获取该URL的资源名
  • String getHost():获取主机名
  • String getPath():获取路径部分
  • int getPort():获取端口号

URL与URLConnection比较

URL和URLConnection的区别在于前者代表一个资源的位置,后者代表一种连接

Java语言提供了两种方法读取数据,一种是通过URL对象直接得到相关的网络信息InputStream is = url.openStream();,另一种是先得到一个URLConnection实例,再得到InputStream或InputStream对象,然后读取数据

URLConnection conn = url.openConnection();
InputStream is1 = conn.getInputStream();
OutputStream os1 = conn.getOutputStream();

前者是一种简单、直接的方法,但缺乏灵活性,并且只能读取只读信息,后者提供了更加灵活有效的方法来读取网络资源

TCP编程

在实际应用中TCP网络程序提供可靠的数据通信,而UDP网络程序则不保证数据的可靠性,但是协议简单、传输速度快(比如用在音视频数据传输,它们不需要很高的可靠性,偶尔丢帧是可以忍受的)

  • TCP是Tranfer Control Protocol的 简称,是一种面向连接的端对端的保证可靠传输的协议。
  • 通过TCP协议传输,得到的是一个顺序的无差错的数据流。

发送方和接收方的成对的两个socket之间必须建立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送 或接收操作。

Socket概述

Socket套接字,就是两台主机之间逻辑连接的端点。TPC协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。Socket本质上就是一组接口,是对TCP/IP协议的封装和应用(程序员层面上)

Socket编程主要涉及到客户端和服务器端两个方面,首先是在服务器端创建一个服务器套接字ServerSocket,并把它附加到一个端口上,服务器从这个端口监听连接。端口号的范围是0到65536,但是0到1024是为特权服务保留的端口号,我们可以选择任意一个当前没有被其他进程使用的端口。

客户端请求与服务器进行连接的时候,根据服务器的域名或者IP地址,加上端口号,打开一个套接字。当服务器接受连接后,服务器和客户端之间的通信就像输入输出流一样进行操作。

Socket整体流程

Socket编程主要涉及到客户端和服务端两个方面,首先是在服务器端创建一个服务器套接字ServerSocket,并把它附加到一个端口上,服务器从这个端口监听连接。端口号的范围是0到65536,但是0到1024是为特权服务保留的端口号,我们可以选择任意一个当前没有被其他进程使用的端口。

客户端请求与服务器进行连接的时候,根据服务器的域名或者IP地址,加上端口号,打开一个套接字。当服务器接受连接后,服务器和客户端之间的通信就像输入输出流一样进行操作。

ServerSocket类

Java.net包中的ServerSocket类用于表示服务器套接字,其主要功能是监听客户端的请求,然后将客户端的请求连接存入队列中,默认请求队列大小是50。

构造方法主要有以下几种形式:

  • ServerSocket():创建非绑定服务器套接字。
  • ServerSocket(int port):创建绑定到特定端口的服务器套接字。Port的取值范围为0-65535之间,0表示使用任意未占用端口,建议使用的端口号大于1024。如果端口已经被占用则会BindException
  • ServerSocket(int port,int backlog):利用指定的backlog创建服务器套接字并将其绑定到指定的本地端口号。
  • ServerSocket(int port,int backlog,InetAdress bindAddress):使用指定的端口、监听backlog和要绑定到本地IP地址创建服务器,适用于计算机有多个网卡、多个IP的情景

端口扫描

	//利用的是ServerSocket在创建时,如果端口已经被占用,则报异常
	for(int i=0;i<=65535;i++) { //端口号0表示使用自由端口,实际上是不能建立连接的
		try {
			ServerSocket ss=new ServerSocket(i);
			ss.close();  //finally
		} catch (Exception e) {
			System.out.println("端口"+i+"已经被占用");
		}
	}

cmd命令查看端口使用情况netstat -an

  • LISTENING是指开放着的,等待连接的
  • ESTABLISHED是正在连接
  • CLOSE_WAIT、TIME_WAIT、SYN_SENT是三次握手四次挥手过程中的某些状态

开放端口包括三种:

  • 0.0.0.0:端口号
  • 127.0.0.1:端口号
  • 主机ip:端口号

区别是0.0.0.0和主机ip后面跟的端口号是对外部网络开放的,是可以通过服务域名、ip可以访问的端口,而127.0.0.1的端口则是只供本机访问的端口。

客户端Socket的构造器

Socket(InetAddress address, int port);创建远程连接到指定服务器

Socket(String host, int prot); 在客户端构建Socket对象,如果构建成功则获取对象,否则ConnectException。参数1为链接主机的名称,也可以使用InetAddress表示IP地址;参数2为链接服务器的监听端口号,要求服务器已经打开的链接端口

Socket(String host, int port, InetAddress localAddr, int localPort)

Socket(InetAddressaddress, int port, InetAddress localAddr, int localPort)

//java.net.ConnectException: Connection refused: connect
	Socket socket=new Socket("127.0.0.1",9999);  //cmd命令netstat -an | findstr "9999"
	System.out.println(socket);

简单的Client/Server程序

C/S架构全称为客户端/服务器体系结构,它是一种网络体系结构,其中客户端是用户运行应用程序的PC端或者工作站,客户端要依靠服务器来获取资源。C/S架构是通过提供查询响应而不是总文件传输来减少了网络流量。它允许多用户通过GUI前端更新到共享数据库,在客户端和服务器之间通信一般采用远程调用RPC或标准查询语言SQL语句。

C/S架构的基本特征:

  • 客户端进程包含特定于解决方案的逻辑,并提供用户与应用程序系统其余部分之间的接口。服务器进程充当管理共享资源(如数据库,打印机,调制解调器或高性能处理器)的软件引擎
  • 前端任务和后端任务对计算资源有着根本不同的要求,例如处理器速度,内存,磁盘速度和容量以及输入/ 输出设备
  • 客户端和服务器的硬件平台和操作系统通常不相同。客户端和服务器进程通过一组明确定义的标准应用程序接口API和RPC进行通信
  • C/S架构的一个重要特征是可扩展性,它们可以水平或垂直缩放。水平扩展意味着添加或删除客户端,工作站只会对性能产生轻微影响。垂直扩展意味着迁移到更大更快的服务器计算机或多服务器中。

服务器端套路

创建ServerSocket对象,绑定监听端口。

通过accept方法监听客户端请求。

连接建立后,通过输入流读取客户端发送的请求信息。

通过输出流向客户端发送响应信息。

关闭响应的资源。

ServerSocket ss=new ServerSocket(8000);
//阻塞当前线程的执行,并等待客户端的链接请求,如果链接成功则返回一个Socket对象
Socket socket=ss.accept();
//通过链接对象Socket可以获取一个输入流和一个输出流
InputStream is=socket.getInputStream();
OutputStream os=socket.getOutputStream();
//具体的数据接收处理逻辑
BufferedReader br=new BufferedReader(new InputStreamReader(is));
//这里使用的是BIO,所以当客户端没有发送数据时,会阻塞等待
String str=br.readLine();
System.out.println("Server:"+str);
//服务器回传系统当前时间
PrintStream ps=new PrintStream(os);
Date now=new Date();
DateFormat df=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String sdate=df.format(now);
ps.println(sdate);
ps.flush();
//发送完毕,关闭流和套接字,完善的写法为try-finally结构
ps.close();
br.close();
socket.close();

客户端套路

创建Socket对象,指明需要连接的服务器的地址和端口号。

连接建立后,通过输出流向服务器发送请求信息。

通过输入流获取服务器响应的信息。

关闭相应资源。

//也可以使用回绕地址127.0.0.1。注意这里的端口号必须和服务器的端口号一致,否则不能链接
//如果链接成功则返回Socket对象
Socket socket=new Socket("localhost",8000);
//通过链接对象Socket可以获取一个输入流和一个输出流
InputStream is=socket.getInputStream();
OutputStream os=socket.getOutputStream();
//具体的请求逻辑
PrintStream ps=new PrintStream(os);
ps.println("Hello Server!");//向服务器发送一个内容为Hello Server!的字符串
//客户端接收服务器传回的系统当前时
BufferedReader br=new BufferedReader(new InputStreamReader(is));
String sdate=br.readLine();//阻塞等待
System.out.println("Client:"+sdate);
//关闭
br.close();
ps.close();
socket.close();

一般的编程实现方式

主线程一直处于阻塞等待状态,如果一旦链接建立成功则启动一个新线程对外提供服务,而主线程继续等待连接请求

依据:监听端口在连接创建期间被占用,连接一旦创建则继续处于空闲状态

public class MyServer {
	public static void main(String[] args) throws Exception {
		ServerSocket server = new ServerSocket(9999);
		while (true) {
			Socket socket = server.accept();
			new MyWorker(socket).start();
		}
	}
}

class MyWorker extends Thread {
	private Socket socket;

	public MyWorker(Socket socket) {
		this.socket = socket;
	}

	@Override
	public void run() {
		BufferedReader br = null;
		PrintStream ps = null;
		try {
			InputStream is = socket.getInputStream();
			OutputStream os = socket.getOutputStream();
			br = new BufferedReader(new InputStreamReader(is));
			ps = new PrintStream(os);
			String ss = br.readLine();
			System.out.println("Client:" + ss);
			if ("hello".equals(ss)) {
				Date now = new Date();
				DateFormat df = new SimpleDateFormat("yyyy-MM-ddE hh:mm:ss");
				ss = df.format(now);
				ps.println(ss);
			}
		} catch (Exception e) {
//			e.printStackTrace();
			throw new RuntimeException(e);
		} finally {
			if (br != null)
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			if (ps != null)
				ps.close();
			if (socket != null)
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}
}

c/s和b/s区别

  • C/S (Client/Server,客户机/服务器)结构,胖客户端应用,是软件系统体系结构的一种。C/S模式简单地讲就是基于企业内部网络的应用系统,大部分的应用逻辑都集中在客户端中,而一般服务器端只提供数据的存储。与B/S(Browser/Server,浏览器/服务器)模式相比,C/S模式的应用系统最大的好处是不依赖企业外网环境,即无论企业是否能够上网,都不影响应用。
  • B/S结构(Browser/Server结构)结构即浏览器和服务器结构,瘦客户端应用,主要逻辑集中在服务器端,客户端一般只包含的简单的显示逻辑。它是随着Internet技术的兴起,对C/S结构的一种变化或者改进的结构。在这种结构下,用户工作界面是通过WWW浏览器来实现,极少部分事务逻辑在前端Browser实现,但是主要事务逻辑在服务器端Server实现,形成所谓三层3-tier结构。

C/S聊天室的实现

服务器端应用包含多个线程,每个Socket对应一个线程,该线程负责读取Socket对应输入流的数据,并将读到的数据向每个Socket发送一次

每个客户端应该包含两个线程,一个负责读取用户键盘输入并将用户输入数据写入Socket对应的流中,一个负责读取Socket对应输入流的数据并打印显示

CharClient

package demo;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;

public class CharClient {
	public static void main(String[] args) throws Exception {
		Socket socket = new Socket("192.168.31.33", 9000);
		OutputStream os = socket.getOutputStream();
		InputStream is = socket.getInputStream();
		Thread t1 = new Thread(new KeyInput(os));
		t1.start();
		Thread t2=new Thread(new ShowInfo(is));
		t2.setDaemon(true);
		t2.start();
		t1.join();
		os.close();
		is.close();
		socket.close();
	}

	static class KeyInput implements Runnable {
		private BufferedReader br;
		private PrintStream ps;

		public KeyInput(OutputStream os) {
			br = new BufferedReader(new InputStreamReader(System.in));
			ps = new PrintStream(os);
		}

		@Override
		public void run() {
			try {
				while (true) {
					String ss = br.readLine();
					if ("quit".equals(ss))
						break;
					if (ss != null && ss.trim().length() > 0)
						ps.println(ss);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	static class ShowInfo implements Runnable {
		private BufferedReader br;

		public ShowInfo(InputStream is) {
			br = new BufferedReader(new InputStreamReader(is));
		}

		@Override
		public void run() {
			try {
				while (true) {
					String ss = br.readLine();
					if (ss != null)
						System.out.println(ss);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
	}
}

CharServer

package demo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

//服务器端应用包含多个线程,每个Socket对应一个线程,该线程负责读取Socket对应输入流的数据,
//并将读到的数据向每个Socket发送一次
public class CharServer {
	private static List<Socket> socketList = new CopyOnWriteArrayList<>();

	public static void main(String[] args) throws Exception {
		ServerSocket ss = new ServerSocket(9000);
		while (true) {
			Socket socket = ss.accept();
			new Thread(new MyRunnable(socket)).start();
			socketList.add(socket);
		}
	}

	static class MyRunnable implements Runnable {
		private Socket socket;
		private BufferedReader br = null;

		public MyRunnable(Socket socket) {
			try {
				this.socket = socket;
				br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			} catch (Exception e) {
			}
		}

		@Override
		// 每个线程都是总自己的socket中读取内容,如果有数据则逐一写出到socketList中的每个Socket。
		// 如果从输入流中读取内容为null则需要将socket从list中剔除
		public void run() {
			try {
				while (true) {
					String ss = br.readLine();
					if (ss == null || ss.trim().length() < 1) {
						break;
					}
					System.out.println("server:"+ss+"==="+socketList);
					InetAddress ia = this.socket.getInetAddress();
					socketList.forEach((Socket obj) -> {
						try {
								PrintStream ps = new PrintStream(obj.getOutputStream());
								ps.println(ia + "说:'" + ss + "'");
						} catch (Exception e) {
							e.printStackTrace();
						}
					});
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				if (this.socket != null)
					socketList.remove(this.socket);
				try {
					this.socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
本文含有隐藏内容,请 开通VIP 后查看