文章目录
背景知识
-
网络编程的三要素:
IP地址:InetAddress: 网络中设备的标识,不易记忆,可用主机名;
端口号:用于标识进程的逻辑地址,不同进程的标识 ;
传输协议:通讯的规则常见协议:TCP,UDP -
UDP协议与TCP协议的区别:
UDP——发短信
将数据源和目的封装成数据包中,不需要建立连接;
每个数据报的大小在限制在64k;
因无连接,是不可靠协议;
不需要建立连接,速度快
TCP——打电话、视频
建立连接,形成传输数据的通道;
在连接中进行大数据量传输;
需要连接所以是可靠协议;
必须建立连接,效率会稍低;
网络编程(Socket编程)
1、Socket是什么
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,是一组接口;
在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议;
应用程序可以通过套接字发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作;套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信;
2、Socket原理
网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字,即就是Socket=IP+端口号;
通信的两端都有Socket、网络通信其实就是Socket间的通信、数据在两个Socket间通过IO传输;
3、Java如何进行网络编程
针对不同协议的Socket,Java给我们提供了相应的Socket;
UDP协议使用的Socket,Java提供了DatagramSocket类来描述;TCP协议使用的Socket,Java使用了Socket类来描述;
动态获取IP、主机名
在Java中,使用InetAddress类来描述IP,它的子类有:Inet4Address、Inet6Address,我们可以直接使用父类;
常用方法:
获取本机的主机名和IP
package org.westos.test;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class test3 {
public static void main(String[] args) {
//可以传递主机名或IP地址获取InetAddress对象
InetAddress ip1 = null;
try {
ip1 = InetAddress.getByName("LAPTOP-B7JPDQ6Q");
System.out.println(ip1.getHostName());//LAPTOP-B7JPDQ6Q
System.out.println(ip1.getHostAddress());//192.168.1.101
System.out.println("===================");
InetAddress ip2 = InetAddress.getByName("192.168.1.101");
System.out.println(ip2.getHostName());//LAPTOP-B7JPDQ6Q
System.out.println(ip2.getHostAddress());//192.168.1.101
System.out.println(ip1==ip2);//false
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
UDP通信
UDP协议传输数据
1、UDP协议发送数据 DatagramSocket类: 此类表示用来发送和接收数据报包的套接字,启用 UDP 广播发送;
构造方法:
常用方法:
public void send(DatagramPacket p) throws IOException
从此套接字发送数据报包;
public void receive(DatagramPacket p) throws IOException
从此套接字接收数据报包。
注意:这里的receive()是一个阻塞式方法,如果没有收到数据,他就会一直阻塞在那里,不往下执行;
2、DatagramPacket类: 此类表示数据报包;
构造方法:
成员方法:
UDP发送数据步骤:
通讯客户端对象DatagramSocket
创建数据报包 DatagramPacket
发送数据
释放资源
客户端:代码里面,我们使用无参构造创建了Socket对象,然后将要发送的IP以及端口号都放在数据报包里面发送出去;
因为UDP协议是一个不可靠协议,就好比你发送信息,有可能你要发送的那个手机号已经为空或者停机了,但是你不需要关心这些,你只负责将信息发送出去;代码里面我们将数据发送出去,但是没有服务端接收,他就会被自动丢弃,而不会返回任何错误;
package org.westos.test;
import java.net.*;
public class test1 {
// DatagramSocket 此类表示用来发送和接收数据报包的套接字。
/* DatagramSocket()
构造数据报套接字并将其绑定到本地主机上任何可用的端口。
void send (DatagramPacket p)
从此套接字发送数据报包。
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
UDP 发短信
将数据源和目的封装成数据包中,不需要建立连接;
每个数据报的大小在限制在64k;
因无连接,是不可靠协议;
不需要建立连接,速度快
*/
public static void main(String[] args) throws SocketException {
//
//1.创建客户端的Soket
DatagramSocket datagramSocket = new DatagramSocket();
//2.发送数据 DatagramPacket 此类表示数据报包 数据报包用来实现无连接包投递服务。
byte[] bytes = "你好:服务端".getBytes();
try {
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("192.168.1.101"), 8888);
datagramSocket.send(datagramPacket);
//3.释放资源
datagramSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
UDP协议接收数据
我们一般是客户端发送数据给服务端,服务端处理数据,那服务端怎么接受数据呢?
步骤:
创建UDP通讯协议服务器端对象(DatagramSocket) 注意要用有参数构造,指定端口号;
创建数据报包,作用用来接收数据;
接收数据 receive(数据包对象) ;
解析数据报包,拿出数据;
释放资源;
服务端:
package org.westos.test;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class test2 {
public static void main(String[] args) {
try {
// DatagramSocket
//1.创建服务端的Socket 并暴露一个端口号 0-65535 0-1023系统端口。
DatagramSocket datagramSocket = new DatagramSocket(8888);
//void receive(DatagramPacket p) 从此套接字接收数据报包。
System.out.println("服务器已经开启....");
/*DatagramPacket( byte[] buf, int length)
构造 DatagramPacket,用来接收长度为 length 的数据包。*/
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
//此方法在接收到数据报前一直阻塞。
datagramSocket.receive(datagramPacket);
//取出数据报包中的数据
byte[] data = datagramPacket.getData();
String address = datagramPacket.getAddress().getHostAddress();
int length = datagramPacket.getLength();
String s = new String(data,0, length);
System.out.println("IP: "+address+"发送msg: "+s);
//释放资源
datagramSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
先开启服务端,客户端再发送数据:执行结果如下:
UDP通信之键盘录入数据的通信
客户端发送数据 发送给服务端,要输入服务端的端口号与IP:
package org.westos.test;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
public class test1 {
public static void main(String[] args) throws SocketException {
//1.创建客户端的Soket
DatagramSocket datagramSocket = new DatagramSocket();
try {
while (true) {
//2.发送数据 DatagramPacket 此类表示数据报包 数据报包用来实现无连接包投递服务。
//通过键盘录入数据不断发送,服务端不断接收
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入要发送的数据: ");
String s = reader.readLine();
byte[] bytes = s.getBytes();
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("LAPTOP-B7JPDQ6Q"), 9999);
datagramSocket.send(datagramPacket);
if ("886".equals(s)){
break;}
}
//3.释放资源
datagramSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
服务端接收数据 暴露自己的端口号:
package org.westos.test;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class test2 {
public static void main(String[] args) {
try {
//创建服务端socket
DatagramSocket datagramSocket = new DatagramSocket(9999);
System.out.println("服务端开始接收数据: ");
while (true){
//接收数据 使用1024字节的数组接收数据 放入datagramPacket的数据包中
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
datagramSocket.receive(datagramPacket);
//查看数据
byte[] data = datagramPacket.getData();
int length = datagramPacket.getLength();
String hostAddress = datagramPacket.getAddress().getHostAddress();
String s = new String(data, 0, length);
System.out.println("IP: "+hostAddress+"发送msg: "+s);
if ("886".equals(s)){
break;}
}
//关闭服务端,一般来说不关闭
datagramSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
客户端与服务端放入线程中开启
模拟两台计算机之间的通信
主线程发数据,子线程收数据
两台计算机的端口号一样,IP不一样,这样才可以通信。如果都通过端口9999来发送和接收数据:
发送数据:输入对方IP与端口号;
接收数据:暴露自己的端口号
例如:计算机A,B的端口号设置为9999
计算机A给计算机B发送数据,此时要输入B的的端口号指定为9999,计算机A接收计算机B的数据,此时暴露自己的端口指定为9999
如果在同一台计算机上测试,模拟AB的端口要不同,否则会端口占用。
下面代码设置A端口为8888,B端口为9999
B开启子线程等待A的数据,主线程发送数据886;A开启子线程接收B的数据886,