目录
1.基本概念
- 定义:网络号+主机号。是四个32位的二进制数据,为方便记忆划分为四个8位。
Demo:
连接特定的 DNS 后缀 . . . . . . . : hollysys.net
本地链接 IPv6 地址. . . . . . . . : fe80::a1e4:76bc:79be:9806%11
IPv4 地址 . . . . . . . . . . . . : 172.21.32.29
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . : 172.21.32.254
这里的172.21.32三位都是网络号,最后的29是主机号。划分的依据是子网掩码中,255段都表示这是网络号段,最后的0是主机号段。
-
要素:
-
IP
-
端口
-
协议
-
-
分类:
类别 | 内容 | 数量 | 使用范围
-------|--------------------------------------------------|-----------------
A类 | 一个网络号+三个主机号 | 2^24个 | 国家使用
B类 | 两个个网络号+两个个主机号 | 2^12个 | 事业单位使用C类 | 三个网络号+一个主机号 | 2^8个 | 私人使用
2.常用函数
package per.liyue.code.net;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class GetIpAddress {
public static void main(String[] args) throws UnknownHostException{
//这个类没有构造,所以静态函数获取对象
InetAddress ip = InetAddress.getLocalHost();
//获取ip地址
System.out.println("我的Ip地址:" + ip.getHostAddress());
//获取机器名
System.out.println("我的Ip机器名:" + ip.getHostName());
//获取其他机器的IP信息-getByName既可以填出机器名,也可以填充IP
String otherIp = "172.21.32.20";
String nameIp = "AMS97-PC";
InetAddress getIP = InetAddress.getByName(otherIp);
System.out.println("目标机器的IP是:" + getIP.getHostName());
InetAddress getName = InetAddress.getByName(nameIp);
System.out.println("目标机器的IP是:" + getName.getHostAddress());
//可以用于获取某个域名下的IP
InetAddress[] a = InetAddress.getAllByName("www.hollysys.com");
for (InetAddress inetAddress : a) {
System.out.println("net:" + inetAddress.getHostAddress());
}
}
}
3.端口
- 端口范围:0~65535,其中
-
0~1023:系统端口:系统紧密绑定
-
1024~49151:注册端口:系统松散绑定
-
1024~65535:可用,如果出错则换另一个
-
4.协议
不同的协议对应不同的Scoket:
*
*
4.1UDP
4.1.1特点
- 不需要建立连接
- 每包大小64K
- 速度快,非可靠(丢包)
- 不区分客户端服务端,之区分发送端和接收端
4.1.2代码步骤
步骤:
-
发送端:
-
建立服务DatagramSocket()
-
创建数据包public DatagramPacket(byte buf[], int length,
InetAddress address, int port) * 发送数据DatagramSocket.send()
-
关闭资源
-
-
接收端:
-
建立服务绑定端口:public DatagramSocket(int port)
-
创建数据包接收:public DatagramPacket(byte buf[], int length)
-
用阻塞方法接收数据:DatagramSocket.receive()
-
关闭资源
-
4.1.3UDPDemo
Demo:
package per.liyue.code.udptest;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
/*
* Udp发送类,用于发送数据
*/
public class UdpSender {
public static void main(String[] args) throws IOException {
//建立socket
DatagramSocket socketSend = new DatagramSocket();
//创建数据包
String sendConcent = "这是我发送的一包数据";
/*
* 这里使用DatagramPacket来存放发送数据,使用的构造是:
* public DatagramPacket(byte buf[], //缓冲数组存放发送数据
* int length, //要用缓冲数组的长度
* InetAddress address,//目标IP地址
* int port //目标端口
* )
*
* */
DatagramPacket packetSend = new DatagramPacket(sendConcent.getBytes(), sendConcent.getBytes().length, InetAddress.getLocalHost(), 10001);
//发送数据
socketSend.send(packetSend);
socketSend.close();
System.out.println("数据已经发送,时间:" + new SimpleDateFormat("yyyy-MM-DD HH-mm-ss").format(new Date()));
}
}
package per.liyue.code.udptest;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
* Udp接收数据类,用于接收数据
*/
public class UdpReceive {
public static void main(String[] args) throws IOException {
//建立socket
DatagramSocket socketReceive = new DatagramSocket(10001);
System.out.println("接收端开启监听...");
//数据包接收
byte[] bufRecive = new byte[1024];
/*
* 用DatagramPacket来接收数组,这里要用构造:
* public DatagramPacket(byte buf[], //缓冲数组来接收,数据实际存放于数组中
* int length) //缓冲数组长度
*/
DatagramPacket packetReceive = new DatagramPacket(bufRecive, bufRecive.length);
//这个方法是阻塞的:This method blocks until a datagram is received。收到数据才执行
socketReceive.receive(packetReceive);
socketSend.close();
//要注意的是
System.out.println("接收到数据:" + new String(bufRecive, 0, packetReceive.getLength()));
System.out.println("接收时间:" + new SimpleDateFormat("yyyy-MM-DD HH-mm-ss").format(new Date()));
}
}
4.1.4通信格式
一般的通信中,数据内容有规定的格式,否则当垃圾数据处理,例如飞秋的为:
version:time:sender:ip:flag:concnet
- 这里的版本号强制使用1.0
- 时间使用系统时间
- 发送人随意
- ip是给对方显示地址
- flag强制为32
- concent是真正的内容
package per.liyue.code.udptest;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
public class UdpSendFeiQ {
public static void main(String[] args) throws IOException {
DatagramSocket send = new DatagramSocket();
String concent = getconcent("飞啊飞");
System.out.println("发送内容: " + concent);
DatagramPacket packet = new DatagramPacket(concent.getBytes(), concent.getBytes().length,
InetAddress.getByName("172.21.32.20"), 2425);
send.send(packet);
send.close();
}
/*
* 飞秋的数据格式:version:time:sender:ip:flag:concnet
* 1.0 32
*/
public static String getconcent(String concent) {
StringBuilder builder = new StringBuilder();
builder.append("1.0:");
builder.append(System.currentTimeMillis() + ":");
builder.append("172.21.32.30:");
builder.append("超人:");
builder.append("32:");
builder.append(concent);
return builder.toString();
}
}
广播地址:在UDP中将主机号修改为255就是网络广播地址,会群发。例如飞秋的地址为:
172.21.32.255
4.1.5 群发Demo:
package per.liyue.code.udptest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.Runnable;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class ChatSender implements Runnable {
@Override
public void run() {
try {
//
DatagramSocket send = new DatagramSocket();
//
BufferedReader read = new BufferedReader(
new InputStreamReader(System.in));
String line = null;
DatagramPacket packet = null;
while((line = read.readLine()) != null){
packet = new DatagramPacket(line.getBytes(), line.getBytes().length,
InetAddress.getLocalHost(), 10001);
send.send(packet);
}
send.close();
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package per.liyue.code.udptest;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class ChatReceive implements Runnable {
@Override
public void run() {
try {
// TODO Auto-generated method stub
DatagramSocket socket = new DatagramSocket(10001);
byte[] concent = new byte[1024];
DatagramPacket packet = new DatagramPacket(concent, concent.length);
boolean flag = true;
while (flag) {
socket.receive(packet);
System.out.println(
packet.getAddress().getHostName() + "说:" +
new String(concent, 0, concent.length));
}
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package per.liyue.code.udptest;
public class ChatMain {
public static void main(String[] args) {
// TODO Auto-generated method stub
ChatReceive receive = new ChatReceive();
ChatSender send = new ChatSender();
new Thread(receive).start();
new Thread(send).start();
}
}
4.1.6丢失数据的情况
- 带宽不够
- cpu处理能力不足
目录
4.2TCP
- 面向连接:建立连接
- 大数据,数据传输时候没有大小限制
- 效率相对低:三次握手机制,可靠
- 区分客户端服务的
4.2.1三次握手机制
发送数据之前要检查通路是否正常,所以是可靠连接。
4.2.2通信范例
- 客户端:发送命令方-》Socket类
- 服务端:接收命令方-》ServerSocket类
4.2.2.1单次通信
Demo:
单次建立连接通信:
package per.liyue.code.tcptest;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
//建立socket链接,指定IP和端口号
Socket socket = new Socket(InetAddress.getLocalHost(), 10001);
//获取到输出流
OutputStream out = socket.getOutputStream();
String conent = "我是客户端,向服务端发送数据了!";
//这里用输出流的写方法发送
out.write(conent.getBytes());
//关闭资源,输出流是从Socket中获取,所以关闭Socket就可以了
socket.close();
}
}
package per.liyue.code.tcptest;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
//建立服务端监听端口
ServerSocket sSocket = new ServerSocket(10001);
//accept方法本身也是阻塞的方法
Socket socket = sSocket.accept();
//因为ServerSocket本身没有流对象的获取,所以要从Socket中获取,客户端发送的时候已经将流发送
InputStream in = socket.getInputStream();
byte[] b = new byte[1024];
int length = in.read(b);
//打印接收内容
System.out.println("服务器接收到的数据:" + new String(b, 0, length));
//关闭资源,输入流和Socket都是通过ServerSocket获取,所以关闭一个即可
sSocket.close();
}
}
注意:为什么不对ServerSocket提供getInputStream和getOutputStream,因为如果ServerSocket本身有提供,在多客户端连接时候,服务端回复的话一个输入输出不能辨别出回复到哪个客户端。所以设计了Socket提供。
4.2.2.2持久化通信
Demo:
package per.liyue.code.tcptest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import com.sun.imageio.plugins.common.InputStreamAdapter;
/*
* TCp实现客户端和服务器互相通信
*/
public class ClientChat {
public static void main(String[] args) {
try {
System.out.println("客户端已经打开,准备发送...");
//创建
Socket socket = new Socket(InetAddress.getLocalHost(), 10001);
OutputStream out = socket.getOutputStream();
//将字符流转换为字节流方便传输
OutputStreamWriter send = new OutputStreamWriter(out);
//获取键盘输入流,将输入的字节流转化为字符流
InputStreamReader in = new InputStreamReader(System.in);
BufferedReader bufRead = new BufferedReader(in);
String line = null;
while((line=bufRead.readLine()) != null){
/*
* 将输入的内容发送
* 注意:因为接收的时候也以readLine来接收,这个方法以回车换行来判断,
* 但是这里的获取输入并不会增加换行回车,所以得手动增加
*/
send.write(line + "
");
//这里不能忘记刷新,否则内容只存在于缓存数组中
send.flush()
}
socket.close();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package per.liyue.code.tcptest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerChat {
public static void main(String[] args) {
try {
System.out.println("服务端已经打开,等待接收...");
//创建ServerSocket
ServerSocket sSocket = new ServerSocket(10001);
//获取Socket对象
Socket socket = sSocket.accept();
//获取输入流
InputStream in = socket.getInputStream();
InputStreamReader inRead = new InputStreamReader(in);
//将收到的字节流转换为字符流方便输出
BufferedReader read = new BufferedReader(inRead);
String line = null;
while ((line = read.readLine()) != null) {
System.out.println("服务端收到:" + line);
}
sSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4.2.2.3持久化相互通信
Demo:
package per.liyue.code.tcptest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import com.sun.imageio.plugins.common.InputStreamAdapter;
/*
* TCp实现客户端和服务器互相通信
*/
public class ClientChat {
public static void main(String[] args) {
try {
System.out.println("客户端已经打开,准备发送...");
/*
* 输出流准备
*/
//创建
Socket socket = new Socket(InetAddress.getLocalHost(), 10001);
OutputStream out = socket.getOutputStream();
//将字符流转换为字节流方便传输
OutputStreamWriter send = new OutputStreamWriter(out);
//获取键盘输入流,将输入的字节流转化为字符流
InputStreamReader in = new InputStreamReader(System.in);
BufferedReader bufRead = new BufferedReader(in);
String line = null;
/*
* 输入准备
*/
//获取输入流
//获取输入内容
BufferedReader readFormServer = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
while((line=bufRead.readLine()) != null){
/*
* 将输入的内容发送
* 注意:因为接收的时候也以readLine来接收,这个方法以回车换行来判断,
* 但是这里的获取输入并不会增加换行回车,所以得手动增加
*/
send.write(line + "
");
//这里不能忘记刷新,否则内容只存在于缓存数组中
send.flush();
//收到服务端
String lineRead = readFormServer.readLine();
System.out.println("客户端收到:" + lineRead);
}
socket.close();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package per.liyue.code.tcptest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerChat {
public static void main(String[] args) {
try {
System.out.println("服务端已经打开,等待接收...");
/*
* 获取输入准备
*/
//创建ServerSocket
ServerSocket sSocket = new ServerSocket(10001);
//获取Socket对象
Socket socket = sSocket.accept();
//获取输入流
InputStream in = socket.getInputStream();
InputStreamReader inRead = new InputStreamReader(in);
//将收到的字节流转换为字符流方便输出
BufferedReader read = new BufferedReader(inRead);
String line = null;
/*
* 输出准备
*/
//获取输出流
OutputStreamWriter out = new OutputStreamWriter(socket.getOutputStream());
//获取准备输出的数据
BufferedReader write = new BufferedReader(new InputStreamReader(System.in));
while ((line = read.readLine()) != null) {
System.out.println("服务端收到:" + line);
String lineWrite = write.readLine();
out.write("服务端说:" + lineWrite + "
");
out.flush();
}
sSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}