Java|网络编程
1.网络编程概述
网络模型一般有两种:
- OSI(Open System Interconnection开放系统互连)七层参考模型
- TCP/IP四层参考模型
网络模型7层概述:
1.物理层
主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。
它的主要作用是传输比特流(就是由1、0转化为电流强弱来进行传输,到达目的地后在转化为1、0)。这一层的数据叫做比特。
2. 数据链路层
主要将从物理层接收的数据进行MAC地址(网卡的地址)的封装与解封装。常把这一层的数据叫做帧。在这一层工作的设备是交换机,数据通过交换机来传输。
3. 网络层
主要将从下层接收到的数据进行IP地址(例192.168.0.1)的封装与解封装。在这一层工作的设备是路由器,常把这一层的数据叫做数据包。
4. 传输层
定义了一些传输数据的协议和端口号(WWW端口80等),如:TCP(传输控制协议,传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报协议,与TCP特性恰恰相反,用于传输可靠性要求不高,数据量小的数据,如QQ聊天数据就是通过这种方式传输的)。
主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组。常常把这一层数据叫做段。
5. 会话层
通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。主要在你的系统之间发起会话或者接受会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是主机名)
6. 表示层
主要是进行对接收的数据进行解释、加密与解密、压缩与解压缩等(也就是把计算机能够识别的东西转换成人能够能识别的东西(如图片、声音等)。
7. 应用层
主要是一些终端的应用,比如说FTP(各种文件下载),WEB(IE浏览),QQ之类的(可以把它理解成我们在电脑屏幕上可以看到的东西.就是终端应用)。
2.网络编程三要素
- IP地址:InetAddress: 网络中设备的标识,不易记忆,可用主机名
- 端口号: 用于标识进程的逻辑地址,不同进程的标识
- 传输协议: 通讯的规则常见协议:TCP,UDP
3.网络编程三要素之IP
3.1 IP概述
IP地址就是给每个连接在Internet上的主机分配的一个32bit地址。
3.2 IP地址的组成
IP地址 = 网络地址+主机地址
A类IP地址:第一段号码为网络地址,剩下的三段号码为本地计算机的号码
B类IP地址:前二段号码为网络地址,剩下的二段号码为本地计算机的号码
C类IP地址:前三段号码为网络地址,剩下的一段号码为本地计算机的号码
3.3 IP地址分类
- A类 1.0.0.1—127.255.255.254
(1)10.X.X.X是私有地址(私有地址就是在互联网上不使用,而被用在局域网络中的地址)
(2)127.X.X.X是保留地址,用做循环测试用的。 - B类 128.0.0.1—191.255.255.254
其中:172.16.0.0—172.31.255.255是私有地址。 - C类 192.0.0.1—223.255.255.254
其中,192.168.x.X是私有地址 - D类 224.0.0.1—239.255.255.254
- E类 240.0.0.1—247.255.255.254
- 特殊地址:127.0.0.1 回环地址,可用于测试本机的网络是否有问题.
3.4 InetAddress类
InetAddress类的概述
为了方便我们对IP地址的获取和操作,java提供了一个类InetAddress 供我们使用。此类表示互联网协议 (IP) 地址。
此类没有构造方法,只有成员方法。
常用的方法:
- public static InetAddress getByName(String host)
在给定主机名的情况下确定主机的 IP 地址,返回一个InetAddress 类的对象。 - static InetAddress getLocalHost()
返回本地主机。
方法1和方法2都可以获取一个InetAddress类的对象。 - public String getHostAddress()
返回 IP 地址字符串(以文本表现形式)。 - public String getHostName()
获取此 IP 地址的主机名
public class MyTest1 {
public static void main(String[] args) throws UnknownHostException {
//获取一个InetAddress类的对象
InetAddress inetAddress = InetAddress.getByName("192.168.43.146");
System.out.println(inetAddress.getHostAddress());
System.out.println(inetAddress.getHostName());
System.out.println("**************");
//获取一个InetAddress类的对象
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost.getHostName());
System.out.println(localHost.getHostAddress());
}
}
输出:
192.168.43.146
LS--20170210IRR
**************
LS--20170210IRR
192.168.43.146
4.网络编程三要素之端口
- 物理端口:网卡口
- 逻辑端口:我们一般指的端口指的就是逻辑端口
a.每个网络程序都会有一个逻辑端口
b.不同进程用不同的逻辑端口标识
c.有效端口:0-65535(两个字节),其中0~1023系统使用或保留端口。
5.网络编程三要素之协议
常用的协议有TCP协议和UDP协议
- TCP
a.建立连接,形成传输数据的通道
b.在连接中进行大数据量传输
c.需要连接所以是可靠协议
d.必须建立连接,效率会稍低 - UDP
a.将数据源和目的封装成数据包中,不需要建立连接
b.每个数据报的大小在限制在64k
c.因无连接,是不可靠协议
d.不需要建立连接,速度快
6.Socket套接字
Socket=IP+端口号
Socket套接字概述:
网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。
Socket套接字概述:
网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。
7.使用UDP协议进行通信
7.1 UDP协议使用到的类
1.DatagramSocket类
此类表示用来发送和接收数据报包的套接字。在 DatagramSocket 上总是启用 UDP 广播发送。
构造方法:
- DatagramSocket() 用于客户端
构造数据报套接字并将其绑定到本地主机上任何可用的端口。 - DatagramSocket(int port) 用于服务端
创建数据报套接字并将其绑定到本地主机上的指定端口。
成员方法:
- close()
关闭此数据报套接字。 - receive(DatagramPacket p)
从此套接字接收数据报包。 - send(DatagramPacket p)
从此套接字发送数据报包。
2.DatagramPacket类
此类表示数据报包。
数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。
构造方法:
-
DatagramPacket(byte[] buf, int length)
构造 DatagramPacket,用来 接收 长度为 length 的数据包。 -
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
构造数据报包,用来将长度为 length 的包 发送 到指定主机上的指定端口号。
成员方法:
- byte[] getData()
返回数据缓冲区。 - int getLength()
返回将要发送或接收到的数据的长度。
7.2 几个案例
7.2.1 简单的UDP协议发送与接收数据
public class Client {
public static void main(String[] args) throws IOException {
DatagramSocket datagramSocket = new DatagramSocket();
byte[] bytes ="我是客户端".getBytes();
InetAddress inetAddress = InetAddress.getByName("192.168.43.146");
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, inetAddress, 6666);
datagramSocket.send(datagramPacket);
datagramSocket.close();
}
}
public class Service {
public static void main(String[] args) throws IOException {
DatagramSocket datagramSocket = new DatagramSocket(6666);
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
System.out.println("服务器已开启...");
datagramSocket.receive(datagramPacket);
byte[] data = datagramPacket.getData();
int length = datagramPacket.getLength();
String hostName = datagramPacket.getAddress().getHostName();
String s = new String(data,0,length);
System.out.println(hostName+"发来如下消息:"+s);
}
}
7.2.2 UDP协议发送端的数据来自于键盘录入
public class Client {
public static void main(String[] args) throws IOException {
DatagramSocket datagramSocket = new DatagramSocket();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true){
System.out.println("输入你要发送的数据:");
String s1 = reader.readLine();
if(s1.equals("886")){
break;
}
byte[] bytes = s1.getBytes();
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("192.168.43.146"), 8888);
datagramSocket.send(datagramPacket);
}
datagramSocket.close();
}
}
public class Service {
public static void main(String[] args) throws IOException {
DatagramSocket datagramSocket = new DatagramSocket(8888);
System.out.println("服务器已开启...");
while (true){
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
datagramSocket.receive(datagramPacket);
byte[] data = datagramPacket.getData();
int length = datagramPacket.getLength();
InetAddress address = datagramPacket.getAddress();
String hostName = address.getHostName();
String hostAddress = address.getHostAddress();
System.out.println(hostAddress + "---" + hostName + "给您发来如下消息:"+new String(data,0,length));
}
}
}
将上述代码改写成多线程的形式
public class MyTest {
public static void main(String[] args) {
//多线程改写demo2
Client client = new Client();
Service service = new Service();
client.start();
service.start();
}
}
public class Client extends Thread{
@Override
public void run() {
try {
DatagramSocket datagramSocket = new DatagramSocket();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true){
System.out.println("输入你要发送的数据:");
String s1 = reader.readLine();
if(s1.equals("886")){
break;
}
byte[] bytes = s1.getBytes();
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("192.168.43.146"), 8888);
datagramSocket.send(datagramPacket);
}
datagramSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class Service extends Thread{
@Override
public void run() {
try {
DatagramSocket datagramSocket = new DatagramSocket(8888);
System.out.println("服务器已开启...");
while (true){
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
datagramSocket.receive(datagramPacket);
byte[] data = datagramPacket.getData();
int length = datagramPacket.getLength();
InetAddress address = datagramPacket.getAddress();
String hostName = address.getHostName();
String hostAddress = address.getHostAddress();
System.out.println(hostAddress + "---" + hostName + "给您发来如下消息:"+new String(data,0,length));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
7.2.3 基于UDP协议的多线程实现聊天室程序
要求:有2个用户A和B,用户A的主线程作为服务器,接收来自B的消息;子线程作为客户端,向B发送信息;B亦然。
A:192.168.43.146 服务器暴露的端口:7777
B:192.168.43.146 服务器暴露的端口:8888
public class A {
//多线程实现聊天室程序
//A:192.168.43.146 服务器暴露的端口:7777
//B:192.168.43.146 服务器暴露的端口:8888
public static void main(String[] args) throws IOException {
new Thread(){
@Override
public void run() {
try {
DatagramSocket datagramSocket = new DatagramSocket();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true){
System.out.println("输入你要发送给B的数据:");
String s1 = reader.readLine();
if(s1.equals("886")){
break;
}
byte[] bytes = s1.getBytes();
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("192.168.43.146"), 8888);
datagramSocket.send(datagramPacket);
}
datagramSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
receiveFun();
}
private static void receiveFun() throws IOException {
DatagramSocket datagramSocket = new DatagramSocket(7777);
System.out.println("A服务器已开启...");
while (true){
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
datagramSocket.receive(datagramPacket);
byte[] data = datagramPacket.getData();
int length = datagramPacket.getLength();
InetAddress address = datagramPacket.getAddress();
System.out.println( "B给您发来如下消息:"+new String(data,0,length));
}
}
}
public class B {
public static void main(String[] args) throws IOException {
new Thread(){
@Override
public void run() {
try {
DatagramSocket datagramSocket = new DatagramSocket();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true){
System.out.println("输入你要发送给A的数据:");
String s1 = reader.readLine();
if(s1.equals("886")){
break;
}
byte[] bytes = s1.getBytes();
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("192.168.43.146"), 7777);
datagramSocket.send(datagramPacket);
}
datagramSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
receiveFun();
}
private static void receiveFun() throws IOException {
DatagramSocket datagramSocket = new DatagramSocket(8888);
System.out.println("B服务器已开启...");
while (true){
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
datagramSocket.receive(datagramPacket);
byte[] data = datagramPacket.getData();
int length = datagramPacket.getLength();
InetAddress address = datagramPacket.getAddress();
System.out.println( "A给您发来如下消息:"+new String(data,0,length));
}
}
}
8.使用TCP协议进行通信
8.1 TCP协议使用到的类
1.Scoket类
此类实现 客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
构造方法
- Socket(InetAddress address, int port)
参数1:服务器的IP地址; 参数2:服务器的端口
创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
成员方法
-
close()
关闭此套接字。 -
InputStream getInputStream()
返回此套接字的输入流。 -
OutputStream getOutputStream()
返回此套接字的输出流。 -
void shutdownInput()
此套接字的输入流置于“流的末尾”。 -
void shutdownOutput()
禁用此套接字的输出流。
2.ServerSocket类
此类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。
构造方法
- ServerSocket(int port)
创建绑定到特定端口的服务器套接字。
成员方法
- Socket accept() 阻塞式方法 此方法只能使用一次!!!
侦听并接受到此套接字的连接。 - close()
关闭此套接字。
8.2 几个案例
8.2.1 简单的TCP协议发送与接收数据
public class Client {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("192.168.43.146", 5555);
OutputStream outputStream = socket.getOutputStream();
outputStream.write("你好".getBytes());
socket.close();
}
}
public class Service {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(5555);
System.out.println("服务器已开启...");
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
String s = new String(bytes, 0, len);
System.out.println(s);
serverSocket.close();
}
}
8.2.2 服务器给客户端一个反馈
客户端发送数据,服务器接受数据并给出反馈
public class Client {
public static void main(String[] args) throws IOException, InterruptedException {
Socket socket = new Socket("192.168.43.146", 5656);
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hi!".getBytes());
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int read = inputStream.read(bytes);
System.out.println(new String(bytes,0,read));
socket.close();
}
}
public class Service {
public static void main(String[] args) throws IOException, InterruptedException {
ServerSocket serverSocket = new ServerSocket(5656);
System.out.println("服务器已经开启....");
Socket accept = serverSocket.accept();
InputStream inputStream = accept.getInputStream();
byte[] bytes = new byte[1024];
int read = inputStream.read(bytes);
String s = new String(bytes, 0, read);
System.out.println(s);
OutputStream outputStream = accept.getOutputStream();
outputStream.write("服务器已经接收到客户端的数据".getBytes());
serverSocket.close();
}
}
8.2.3 客户端键盘录入服务器写到文本文件
public class Client {
public static void main(String[] args) throws IOException, InterruptedException {
//客户端键盘录入服务器写到文本文件
Socket socket = new Socket("192.168.43.146", 5656);
while (true){
OutputStream outputStream = socket.getOutputStream();
Scanner scanner = new Scanner(System.in);
System.out.println("请输入:");
String s = scanner.nextLine();
outputStream.write(s.getBytes());
if(s.equals("886")){
break;
}
}
socket.close();
}
}
public class Service {
public static void main(String[] args) throws IOException, InterruptedException {
ServerSocket serverSocket = new ServerSocket(5656);
System.out.println("服务器已经开启....");
Socket accept = serverSocket.accept();
FileOutputStream fileOutputStream = new FileOutputStream("aaa.txt");
while (true){
InputStream inputStream = accept.getInputStream();
byte[] bytes = new byte[1024];
int read = inputStream.read(bytes);
fileOutputStream.write(bytes,0,read);
fileOutputStream.write("\n".getBytes());
fileOutputStream.flush();
}
}
}
8.2.4 客户端读取文本文件服务器控制台输出
public class Client {
public static void main(String[] args) throws IOException {
//客户端读取文本文件服务器控制台输出
Socket socket = new Socket("192.168.43.146", 5656);
OutputStream outputStream = socket.getOutputStream();
//包装
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
BufferedReader reader = new BufferedReader(new FileReader("aaa.txt"));
String line=null;
while ((line=reader.readLine())!=null){
writer.write(line);
writer.newLine();
writer.flush();
}
//手动写一个结束标记
writer.write("over");
writer.newLine();
writer.flush();
socket.close();
}
}
public class Service {
public static void main(String[] args) throws IOException {
//客户端读取文本文件服务器控制台输出
ServerSocket serverSocket = new ServerSocket(5656);
System.out.println("服务器已经开启....");
Socket accept = serverSocket.accept();
InputStream inputStream = accept.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String s1 = new String();
s1=null;
while (true){
s1=reader.readLine();
if(s1.equals("over")){
break;
}
System.out.println(s1);
}
serverSocket.close();
}
}
8.2.5 TCP上传文本文件并给出反馈
TCP传输容易出现的问题:
客户端连接上服务端,两端都在等待,没有任何数据传输。
原因分析:
因为read方法或者readLine方法是阻塞式。
解决办法:
- 1.自定义结束标记
- 2.使用shutdownInput,shutdownOutput方法。
方式2的代码:
public class Client {
public static void main(String[] args) throws IOException {
//TCP协议上传文本文件并给出反馈
Socket socket = new Socket("192.168.43.146", 5656);
OutputStream outputStream = socket.getOutputStream();
//包装
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
BufferedReader reader = new BufferedReader(new FileReader("aaa.txt"));
String line=null;
while ((line=reader.readLine())!=null){
writer.write(line);
writer.newLine();
writer.flush();
}
socket.shutdownOutput();//禁用此套接字的输出流
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int read = inputStream.read(bytes);
System.out.println(new String(bytes, 0, read));
socket.close();
writer.close();
reader.close();
}
}
public class Service {
public static void main(String[] args) throws IOException, InterruptedException {
//TCP协议上传文本文件并给出反馈
ServerSocket serverSocket = new ServerSocket(5656);
System.out.println("服务器已经开启....");
Socket accept = serverSocket.accept();
InputStream inputStream = accept.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
BufferedWriter writer = new BufferedWriter(new FileWriter("aaa(2).txt"));
String line=null;
while ((line=reader.readLine())!=null){
writer.write(line);
writer.newLine();
writer.flush();
}
OutputStream outputStream = accept.getOutputStream();
outputStream.write("文件上传完毕".getBytes());
serverSocket.close();
writer.close();
reader.close();
}
}
8.2.6 多线程改进上传文本文件(空)
一个服务器,开启客户端的多个线程,同时向服务器上传数据。
在这里插入代码片
9 常用DOS 命令
DOS 命令 | 意义 |
---|---|
ipconfig -all | 获取本地IP,主机名,MAC地址 |
ing -a x.x.x.x | 获取x.x.x.x的主机名 |
net view | 获取局域网中的所有主机名 |
arp -a | 获取本局域网中的所有IP地址和物理地址 |
nbtstat -a 主机名 | 获取MAC地址 |
https://blog.youkuaiyun.com/havedream_one/article/details/47071393 参考这篇文档可以提取局域网中其他主机的IP地址