1、IP地址
2、端口
3、协议
IP地址就是设备(电脑、手机、平板、冰箱、空调、台灯)在网络中的唯一标识(通过IP地址访问设备)
IP地址的两大分类
IPV4(主流的网络地址格式)
IPV6
DOS命令
ipconfig -all
ping xxx.xxx.xxx.xxx
netstat -ano
特殊的IP地址
127.0.0.1 - 本地的回环地址
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* @author Mr.乐
* @Description InetAddress
*/
public class Demo01 {
public static void main(String[] args) throws UnknownHostException {
//将ip地址转换为一个对象
// InetAddress address = InetAddress.getByName("故离");//通过主机名获取
InetAddress address = InetAddress.getByName("127.0.0.1"); //通过回环地址获取
// InetAddress address = InetAddress.getByName("192.168.8.1"); // 外网地址
System.out.println(address);
//获取进程所在机器的计算机的名字
System.out.println(address.getHostName());
//获取主机的IP地址
System.out.println(address.getHostAddress());
}
}
端口就是一个应用程序(进程/线程)在设备(本机)中的唯一标识
端口号:用两个字节的整数来表示的(unsigned short:0~65535),其中0 ~ 1024之间的端口号尽量不要使用,因为很多知名的网络服务商都会在这个范围内选择端口号(被占用的几率比较大),建议使用10000以上。如果被占用的话,再换一个。
计算机网络中,连接和通信的规则
UDP
User Datagram Protocol 用户数据包协议
UDP协议是一种无连接的通信协议,即在数据传输的时候,数据的发送端和接收端不会建立所谓的逻辑连接,简单来说就是一台计算机向另一台极端及发送数据时,发送端不会确认接收端是否存在,都会将数据发送出去。同样接收端也不会向发送端反馈任何的接收数据的内容。
UDP协议消耗的资源相对来说较小,通信效率很高,但是不安全的。
TCP
Transmission Control Protocol 传输控制协议
TCP协议是一种面向连接/安全的通信协议,在数据传输之前,发送端和接收端会各自创建一个逻辑连接,然后再进行数据传输。
UDP在通信的时候在两端各自创建一个Socket(套接字)对象,但是这两个Socket只是用来发送和接收数据的对象,因此对于UDP协议通信的双方而言,没有所谓的客户端和服务器端。
发送的步骤
1、创建发送端的Socket对象 - DatagramSocket
2、创建数据,并把数据打包
3、调用发送方法 - send
4、关闭发送
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* @author Mr.乐
* @Description UDP发送端
*/
public class UDPSend {
public static void main(String[] args) throws IOException {
//1、创建发送端的Socket对象 - DatagramSocket
DatagramSocket socket=new DatagramSocket();
// 2、创建数据,并把数据打包
byte[] bytes = "Hello UDP Send Messages!".getBytes();
int length = bytes.length;
InetAddress address = InetAddress.getByName("127.0.0.1");
int port=1314;
DatagramPacket packet=new DatagramPacket(bytes,length,address,port);
//3、调用发送方法 - send
socket.send(packet);
// 4、关闭发送
socket.close();
}
}
接收数据的步骤
1、创建接收端的Socket对象
2、创建一个数据包,用于接收数据
3、调用接收方法 - receive
4、解析数据包
5、关闭接收
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* @author Mr.乐
* @Description UDP接收数据
*/
public class UDPReceive {
public static void main(String[] args) throws Exception {
// 1、创建接收端的Socket对象
DatagramSocket ds = new DatagramSocket(1314);
// 2、创建一个数据包,用于接收数据
byte[] bytes = new byte[1024];//创建接收数据缓冲区,如果实际数据长度超过了范围,则会被截断
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
// 3、调用接收方法 - receive
ds.receive(dp);
// 4、解析数据包
byte[] data = dp.getData();//获取数据包中的数据
int length = dp.getLength();//获取发送过来的数据的实际长度
String str = new String(data, 0, length);//将数据转换成字符串
System.out.println(str);
// 5、关闭接收
ds.close();
}
}
需求
1、UDP发送数据:数据来源于键盘输入,直到输入over的时候就结束数据的发送
2、UDP接收数据:因为不知道发送端什么时候停止发送,所以使用死循环接收数据
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* @author Mr.乐
* @Description 发送端获取键盘输入发送数据
*/
public class UDPSend {
public static void main(String[] args)throws Exception {
DatagramSocket ds = new DatagramSocket();//创建Socket对象
//自己封装的标准键盘输入流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = br.readLine()) != null){
if("over".equals(line))
break;
//如果用户输入的不是over,则需要把数据打包发送到接收端
byte[] bytes = line.getBytes();
int length = bytes.length;
InetAddress address = InetAddress.getByName("127.0.0.1");
int port = 9527;
DatagramPacket dp = new DatagramPacket(bytes, length, address, port);//打包发送的数据
ds.send(dp);//发送数据
}
ds.close();//关闭对象
}
}
----------------------------------------------------------------------------
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* @author Mr.乐
* @Description 接收端循环接收数据
*/
public class UDPReceive {
public static void main(String[] args) throws Exception {
DatagramSocket ds=new DatagramSocket(9527);
while (true){
byte[] bytes = new byte[1024];//创建数据缓冲区
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);//创建数据包用来接收数据
ds.receive(dp);//接收数据
System.out.println("接收到:" + new String(bytes,0,dp.getLength()));
}
}
}
TCP通信是一种可靠的网络编程协议,它会在通信的两端各自创建一个Socket对象,从而在通信的两端形成虚拟的网络连接,一旦建立了这种网络间接,两端的程序就可以通过网络IO流来进行数据的通信。
发送步骤
1、创建客户端的Socket对象 - Socket
2、获取输出流
3、写数据
4、释放资源
import java.io.OutputStream;
import java.net.Socket;
/**
* @author Mr.乐
* @Description 客户端发送数据
*/
public class TCPSend {
public static void main(String[] args) throws Exception {
//创建Socket对象
Socket socket = new Socket("127.0.0.1", 1314);
//通过Socket对象获取输出流
OutputStream os = socket.getOutputStream();
//通过输出流对象写数据,
os.write("Hello TCP Send Messages!~".getBytes());
//释放资源
socket.close();
os.close();
}
}
接收数据的步骤
1、创建服务器端的Socket对象 - ServerSocket
2、获取通信用的Socket对象
3、获取输入流
4、读数据
5、处理数据
6、释放资源
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author Mr.乐
* @Description TCP服务器端接收数据
*/
public class TCPReceive {
public static void main(String[] args) throws Exception {
//1、创建服务器端的Socket对象 - ServerSocket
ServerSocket serverSocket = new ServerSocket(1314);
// 2、获取通信用的Socket对象
Socket socket = serverSocket.accept();//等待请求被连接,阻塞式方式,获取用来通信的socket对象
// 3、获取输入流
InputStream is = socket.getInputStream();
// 4、读数据
byte[] bytes = new byte[1024];
int length = is.read(bytes);//通过输入流读数据,返回的是实际数据的的长度
// 5、处理数据
String data = new String(bytes, 0, length);
System.out.println("接收到客户端发来的消息:" + data);
// 6、释放资源
serverSocket.close();
socket.close();
is.close();
}
}
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Set;
/**
* @author Mr.乐
* @Description 转发服务器
*/
public class Server {
public static void main(String[] args) throws Exception {
//创建UDP接收和发送对象
DatagramSocket dsSend = new DatagramSocket();//发送对象
DatagramSocket dsRec = new DatagramSocket(1314);//接收对象
byte[] bytes = new byte[1024];//创建聊天数据缓冲区
//接收并转发数据
while (true){
//创建接收数据包
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//接收数据
dsRec.receive(dp);//接收到数据后,将数据转发给其它客户端
int length = dp.getLength();//接收到的数据的实际长度
InetAddress address = InetAddress.getByName("127.0.0.1");//IP地址
//转发给每个端口号,获取客户端的端口号
Set portSet = Global.portMap.keySet();
for (Integer port : portSet) {
dsSend.send(new DatagramPacket(bytes,length,address,port));
}
System.out.println("已成功转发一条信息");
}
}
}
---------------------------------------------------------------------
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* @author Mr.乐
* @Description
*/
public class Send implements Runnable{
@Override
public void run() {
try {
//创建发送的Socket对象
DatagramSocket ds = new DatagramSocket();
//封装一个键盘输入流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line;
while((line=br.readLine())!=null){
if ("exit".equals(line))
System.exit(0);//如果用户输入exit,直接结束程序
byte[] bytes = (Thread.currentThread().getName() + "##@@##" + line).getBytes();
DatagramPacket dp=new DatagramPacket(bytes,bytes.length,
InetAddress.getByName("127.0.0.1"),1314);
ds.send(dp);//发送数据
}
}catch (Exception e){
e.printStackTrace();
}
}
}
--------------------------------------------------------------
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
* @author Mr.乐
* @Description
*/
public class Receive implements Runnable{
//定义端口号用于接收线程接收数据
private Integer port;
public Receive(Integer port){
this.port = port;
}
@Override
public void run() {
try {
DatagramSocket ds=new DatagramSocket(port);
System.out.println(Thread.currentThread().getName() + "- 登录成功!~");
while(true){
//创建接收数据缓冲区
byte[] bytes = new byte[1024];
//创建接收数据包
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
ds.receive(dp);//接收数据
//此时接收到的信息的格式:用户名##@@##信息
String recStr = new String(bytes, 0, dp.getLength());
//拆分成用户名和信息的字符数组
String[] info = recStr.split("##@@##");
//如果发送了空的信息,可能没有info[1]的成员
String mes = info.length > 1 ? info[1] : "";
//过滤自己发送的信息字节接收,并且同时保证信息不为空
if(!Thread.currentThread().getName().equals(info[0]) && !mes.equals("")){
System.out.println("接收到 - " + info[0] + "->" + mes);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
----------------------------------------------------------------------
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author Mr.乐
* @Description 客户端类
*/
public class ChatRoomRun {
public static void main(String[] args) {
//提示用户登录,获取用户序号
int index = getUser();
int port = 20000 + index;//获取用户对应的端口号
//获取用户名
String userName = Global.portMap.get(port);
Thread sendThread = new Thread(new Send(), userName);//创建发送线程
Thread recThread = new Thread(new Receive(port), userName);//创建接收线程
//开启线程
sendThread.start();
recThread.start();
}
private static int getUser() {
System.out.println("用户列表如下:");
Set portSet = Global.portMap.keySet();//获取端口号的集合
int index = 1;
for (Integer port : portSet) {
System.out.println(index++ + ":" +Global.portMap.get(port));
}
do{
System.out.println("请输入正确的用户序号:");
index = new Scanner(System.in).nextInt();//获取用户输入的序号
}while (index < 1 || index > 4);
return index;
}
}