学习目录
- 前言
-
- 一、重温计网
- 二、从代码到网络
-
- 1.InetAddress类
- 2.Datagram类
-
- 2.1DatagramPacket类
- 2.2DatagramSocket类
- 三、UDP一发一收模型
- 四、UDP多发多收模型
- 五、TCP一发一收模型
- 六、TCP多发多收模型
- 七、多线程实现多用户通信
- 面试八股文整理
前言
在计算网络的基础上,设备之间的通信是通过网络实现数据传输,数据通过网络从一个设备传输到另一个设备。我们知道在以前的研究中让我们实现网络通信,其实
一、重温计网
:覆盖面最小,只覆盖一间教室或一间机房 :覆盖面大,可覆盖城市 :万维网是广域网的代表 :网络中的设备标记,每个主机都有唯一的标记ip地址,主要分为IPv4,IPv6 :公网地址、私网地址、特殊地址ip地址:127.0.0.1或者localhost表示本机 :解决记忆ip地址难的问题导致域名,比如www.baidu.com :标识计算机上特定的网络程序,被规定为一个16位的二进制,范围是0~65535,比如tomcat——8080、mysql——3306、sqlserver——1433… :周知端口:0~1023,被预定义的知名应用占用(如:HTTP占用80,FTP占用21) :1024~49151,分配给用户流程或某些应用程序。Tomcat占用8080,MySQL占用3306) :49152~65535之所以被称为动态端口,是因为它通常不固定分配某个过程,而是动态分配 :当我们自己开发的程序选择注册端口时,同一设备不能有相同的程序,否则错误报告 : 1.OSI模型自上而下:应用层-表示层-会话层-传输层-网络层-数据链路层-物理层 2.TCP/IP模型自顶向下:应用层-传输层(TCP)-网络层(IP)-物理 数据链路层 :常见的协议包括网络中数据传输的规则UDP协议和TCP协议。 : 使用TCP协议前必须建立TCP连接是一种面向连接的可靠通信协议。在传输数据通道传输之前,采用三次握手的方式是可靠的,TCP协议通信的两个应用程序:客户端和服务端可以在连接中传输大数据。传输完成后,需要释放已建立的连接,效率低下 TCP协议通信: : 用户数据报告协议将数据、源和目的包装成数据包,无需建立连接。每个数据报告的大小限制在64K内部,不适合传输大量数据,因为不需要连接,所以不可靠,发送数据不需要释放资源(因为不是面向连接),速度快,效率高,但不安全,容易丢失数据 : 套接字(Socket)开发网络应用程序被广泛使用,从而成为实际标准。通信的两端都有Socket,它是两台机器之间通信的终点。网络通信实际上是Socket间的通信,Socket允许程序将网络连接视为一流,数据在两个Socket间通过IO传输。一般来说,主动发起通信的应用程序属于客户端,等待通信请求为服务端
二、从代码到网络
网络编程的本质是面向接口和类编程,Java核心类库为我们提供了面向通信协议和网络模型编程的渠道,这是面向对象思想的体现
1.InetAddress类
InetAddress类用于表示Internet协议(IP)地址,是序列接口Serializable同时也是实现类Inet4Address、Inet6Address的父类
名称 | 说明 |
---|---|
public static InetAddress getLocalHost() | 返回主机地址对象 |
public static InetAddress getByName(String host) | 指定主机IP参数是域名或地址对象IP地址 |
public String getHostName() | 获取此IP地址主机名 |
public String getHostAddress () | 返回IP地址字符串 |
public boolean isReachable(int timeout) | 在指定毫秒内连接IP地址对应的主机连接返回true |
代码演示:
// 1.获取本机地址对象。 InetAddress ip1 = InetAddress.getLocalHost(); System.out.println(ip1.getHostName()); System.out.println(ip1.getHostAddress()); // 2.获取域名ip对象
InetAddress ip2 = InetAddress.getByName("www.baidu.com");
System.out.println(ip2.getHostName());
System.out.println(ip2.getHostAddress());
// 3.获取公网IP对象。
InetAddress ip3 = InetAddress.getByName("112.80.248.76");
System.out.println(ip3.getHostName());
System.out.println(ip3.getHostAddress());
2.Datagram类
2.1DatagramPacket类
主要用于创建发送端数据包对象 public DatagramPacket(byte[] buf,int length,InetAddness address,int port)
buf | 要发送的内容,字节数组 |
---|---|
length | 要发送内容的字节长度 |
address | 接收端的IP地址对象 |
port | 要发送的内容,字节数组 |
成员方法:public int getLength()
来获取实际收到的字节数
2.2DatagramSocket类
主要用于发送端和接收端对象
public DatagramSocket() | 创建发送端的Socket对象,系统会随机分配一个端口号。 |
---|---|
public DatagramSocket(int port) | 创建接收端的Socket对象并指定端口号 |
public void send(DatagramPacket dp)
用于发送数据包 public void receive(DatagramPacket dp)
用于接收数据包
三、UDP一发一收模型
// 1、创建发送端对象 发送端自带默认的端口号
DatagramSocket socket = new DatagramSocket(6666);
// 2、创建一个数据包对象封装数据
byte[] buffer = "hello java".getBytes();
DatagramPacket packet = new DatagramPacket( buffer, buffer.length,InetAddress.getLocalHost() , 8888);
// 3、发送数据出去
socket.send(packet);
// 4、关闭连接
socket.close();
// 1、创建接收端对象:注册端口
DatagramSocket socket = new DatagramSocket(8888);
// 2、创建一个数据包对象接收数据
byte[] buffer = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// 3、等待接收数据。
socket.receive(packet);
// 4、取出数据即可
int len = packet.getLength();
String rs = new String(buffer,0, len);
System.out.println("收到:" + rs);
// 获取发送端的ip和端口
String ip =packet.getSocketAddress().toString();
System.out.println("对方地址:" + ip);
int port = packet.getPort();
System.out.println("对方端口:" + port);
socket.close();
四、UDP多发多收模型
// 1、创建发送端对象:发送端自带默认的端口号
DatagramSocket socket = new DatagramSocket(7777);
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String msg = scanner.nextLine();
if("back".equals(msg)){
System.out.println("离线成功!");
socket.close();
break;
}
// 2、创建一个数据包对象封装数据
byte[] buffer = msg.getBytes();
DatagramPacket packet = new DatagramPacket( buffer, buffer.length,InetAddress.getLocalHost() , 8888);
// 3、发送数据出去
socket.send(packet);
}
// 1、创建接收端对象:注册端口
DatagramSocket socket = new DatagramSocket(8888);
// 2、创建一个数据包对象接收数据
byte[] buffer = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while (true) {
// 3、等待接收数据
socket.receive(packet);
// 4、取出数据即可
// 读取多少倒出多少
int len = packet.getLength();
String rs = new String(buffer,0, len);
System.out.println("收到了来自:" + packet.getAddress() +", 对方端口是" +packet.getPort() +"的消息:" + rs);
}
之所以可以接收很多发送端的消息是因为服务器的接收端只负责接收数据包 :发送端发送的数据包的目的地写的是广播地址,且指定端口,本机所在的网段的机群程序注册对应端口 :发送端的数据包目的地是组播IP,接收端必须绑定改组播IP,端口要注册发送端的目的端口
五、TCP一发一收模型
:
// 1、创建Socket通信管道请求有服务端的连接
Socket socket = new Socket("127.0.0.1", 7777);
// 2、从socket通信管道中得到一个字节输出流 负责发送数据
OutputStream os = socket.getOutputStream();
// 3、把低级的字节流包装成打印流
PrintStream ps = new PrintStream(os);
// 4、发送消息
ps.println("hello java");
ps.flush(); //刷新!
// 关闭资源
socket.close();
: 注册端口调用方法接收就完事儿
// 1、注册端口
ServerSocket serverSocket = new ServerSocket(7777);
// 2、必须调用accept方法:等待接收客户端的Socket连接请求,建立Socket通信管道
Socket socket = serverSocket.accept();
// 3、从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
// 4、把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 5、按照行读取消息
String msg;
if ((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + "收到的内容是:: " + msg);
}
六、TCP多发多收模型
:
// 1、创建Socket通信管道请求有服务端的连接
Socket socket = new Socket("127.0.0.1", 7777);
// 2、从socket通信管道中得到一个字节输出流 负责发送数据
OutputStream os = socket.getOutputStream();
// 3、把低级的字节流包装成打印流
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请发送:");
String msg = sc.nextLine();
// 4、发送消息
ps.println(msg);
ps.flush();
}
// 关闭资源。
socket.close();
: 接收请求,io读取
// 1、注册端口
ServerSocket serverSocket = new ServerSocket(7777);
while (true) {
// 2、必须调用accept方法:等待接收客户端的Socket连接请求,建立Socket通信管道
Socket socket = serverSocket.accept();
// 3、从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
// 4、把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 5、按照行读取消息
String msg;
while ((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + "收到的内容是:: " + msg);
}
}
上述一发一收、多发多收的本质还是客户端发,服务器端接、客户端循环发,服务器端循环接 服务器端是单线程的,每次只能处理一个客户端的消息
七、多线程实现多用户通信
服务器端主线程定义一个循环体用来接收客户端Socket连接,每连接到一个Socket通道后分配一个独立的线程来处理它,如图所示 1.此时的客户端还是在不断地发消息
// 1、创建Socket通信管道请求有服务端的连接
Socket socket = new Socket("127.0.0.1", 7777);
// 2、从socket通信管道中得到一个字节输出流 负责发送数据
OutputStream os = socket.getOutputStream();
// 3、把低级的字节流包装成打印流
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String msg = sc.nextLine();
// 4、发送消息
ps.println(msg);
ps.flush();
}
// 关闭资源。
socket.close();
2.这时的服务器端要同时处理多个客户端的通信需求 首先把主线程写好,无限循环用来接收客户端的消息
// 1、注册端口
ServerSocket serverSocket = new ServerSocket(7777);
// 定义一个死循环由主线程负责不断的接收客户端的Socket管道连接。
while (true) {
// 2、每接收到一个客户端的Socket管道,交给一个独立的子线程负责读取消息
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress()+ "鸡汤来咯~");
// 3、开始创建独立线程处理socket
new ServerReaderThread(socket).start();
}
3.实现Thread类中的子线程用来处理主线程随机分配下来的的任务,主线程无限的接,接到一个消息主线程就new一个子线程然后start()
public class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;}
@Override
public void run() {
// 3、从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
// 4、把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 5、按照行读取消息
String msg;
while ((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + "收到的消息是:: " + msg);
}
}
}
上述的多用户通信模式存在一定的问题 由于是N-N的关系,所以并发问题比较严重,这个问题就等到学完并发编程再回来优化吧
面试八股文整理
:假如子网掩码维255.255.255.245有多少个ip可用? 答:
:判断192.162.1.1 是A、B、C类那种网络ip地址? 答:
:TCP和UDP有什么区别? 答:
:进程间通讯的方式有哪些? 答:
锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。 4.消息队列:消息队列是有消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。 5.信号 ( sinal ) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。 6.共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。 7.套接字( socket ) :套接字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信
:tcp连接建立时三次握手的具体过程,以及每一步原因? 答:
——部分题目引自《常见网络编程面试题整理》作者:繁华如梦