首页 > 技术文章 > JAVA 网络编程

kingwz 2021-12-18 14:52 原文

网络基础知识

网络的分类:

(按照规模)
局域网(LAN): 指在一个较小地理范围内的各种计算机网络设备互连在一起的通信网络,可以包含一个或多个子网,通常局限在几千米的范围之内。
城域网(MAN): 主要由城域范围内的各局域网之间互连而构成的。
广域网(WAN): 是由相距较远的局域网或城域网互联而成,通常是除了计算机设备以外,还要涉及一些电信通信方式。

网络编程三要素:

IP地址

用于标志网络中的一个通信实体,这个通信实体可以是一台主机,也可以是一个打印机,或者是路由器的某一个端口。
IP地址被分成了A、B、C、D、E五类。
A类:10.0.0.0 —— 10.255.255.255
B类:172.16.0.0 —— 172.31.255.255
C类:192.168.0.0 —— 192.168.255.255

IP地址是一个32位的二进制数,它通常采用点分( 十 )进制表示。
作用:
用于在网络中找到主机
注意:
在TCP/IP四层协议中,IP协议位于哪个层次( 网络层)

查看自己的ip地址

运行cmd输入:ipconfig/all便会显示,我们需要的是IPv4/6地址那行。

端口

是一个16位的整数,用于表示数据交给哪个通信程序处理。
因此,端口就是应用程序与外界交流的出入口,它是一宗抽象的软件结构,包括一些数据结构和I/O缓冲区。
端口号可以从0到65535,通常被分为三类:
公认端口:0 — 1023;
注册端口:1024 — 49151;
动态/私有端口:49152 — 65535
作用:
区分同一台主机中的不同应用。
注意:
在TCP/IP四层协议中,端口位于传输层
B.
端口号可以从0到65535取值
C.
自定义端口应该避免从0-1023中取值,因为这些端口是熟知端口
D.
端口对应着一个应用程序,通过端口号,主机间才能进行通信

传输协议

概述:
数据在传输过程中接收、发送,处理等操作是有规则。不同的场景有不同的协议,往往不同的协议协同合作维护网络编程的环境。
OSI分层模型和TCP/IP分层模型
image
相同颜色的色块,代表着相同的作用
传输层有:TCP协议 UDP协议

常见网络操作

URL类

类 URL 代表一个统一资源定位符,它是指向互联网“资源”的指针,通过URL类可以直接读取或写入网络上的资源。
** 构造方法:**
URL(String spec) //根据 String 表示形式创建 URL 对象。(常用) spec代表网页地址
等等
获取网络资源方法:
InputStream openStream();//连接此URL ,并返回一个输入流,以便从该连接读取。

测试代码

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

public class URLtest {
         public static void main(String[] args) throws IOException {
			URL url=new URL("https://www.cnblogs.com/kingwz/p/15705028.html");
			             //协议名称  主机名(IP地址对应的名字)(由DNS服务器进行转换)         
			InputStream is1=url.openStream();
			int len =0;
			byte[] bytes=new byte[1024];
			FileOutputStream fos1=new FileOutputStream("d:\\data.html");
			while( (len=is1.read(bytes) )!=-1) {
		           System.out.println(bytes);
		           fos1.write(bytes,0,len);
			}
			is1.close();
			fos1.close();
		}
}

获取属性方法:
对于一个网页:
https://home.cnblogs.com/user/Search.aspx?key=kingwzun
//协议:https
//主机:home.cnblogs.com
//端口:-1
//默认端口:443
//路径:/user/Search.aspx
//资源:key=kingwzun
获取URL对象的属性
String getProtocol(): //返回表示URL中的协议
getHost(): //返回主机名。(IP地址对应的名字)(由DNS服务器进行转换)
getDefaultPort()://返回默认的端口号。(与协议有关系)
getPort(): //返回端口号,默认为-1。(如果网址中没有点明,查找不到,返回-1)
getPath(): //返回指定资源的路径
getQuery();//返回URL指定的资源

InetAddress类

import java.net.InetAddress;
import java.net.UnknownHostException;

public class IPtest {
	public static void main(String[] args) throws UnknownHostException {
		InetAddress ip1 = InetAddress.getByName("www.cnblogs.com");// 获取网址的ip地址,注意www开头
		System.out.println(ip1);
		InetAddress myip1 = InetAddress.getLocalHost();// 获取本机ip地址,方法1
		System.out.println(myip1);
		System.out.println("==========================");
		byte[] bytes = { 127, 0, 0, 1 };
		InetAddress myip2 = InetAddress.getByAddress(bytes);
		System.out.println(myip2);// 获取本机ip地址,方法2
		InetAddress myip3 = InetAddress.getByName("localhost");
		System.out.println(myip3);// 获取本机ip地址,方法3
	}
}

UDP和TCP协议

概述

概述:
是传输层的协议,是数据端到端的协议

TCP

(Transfer Control Protocol)(传输控制协议)
类似打电话
特点:

  1. TCP是一个基于连接的协议,要求对方必须在线并且连接成功。
  2. 是保证可靠传输的协议,通过TCP协议传输,得到的是一个顺序的无差错的数据流。
  3. 区分客户和服务,把发送数据段叫做客户端,接收数据端就做服务端。

UDP

(User Datagram Protocol)
类似QQ,写信等
特点:

  1. 是面向无连接的协议,不用端和端之间连接就进行数据的传输。
    • 不用管对方在不在线,直接操作数据,但是数据发出去之后有可能收不到,安全性低,效率高。
  1. UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方。
  2. 不区分客户端和服务端,只有发送端和接收端。
  3. 如果接受的字节数组小,会丢失数据

Socket(套接字)

作用:
传输层编程是端到端的编程,套接字就是端和端进行交流的中间服务
(类似与快递/邮递员)

Socket编程在不同的协议下使用的套接字不一样。
UDP协议:DatagramSocket
TCP协议:
客户端:Socket
服务端:ServerSocket

shutdownOutput

Socket.shutdownOutput()
被称为"半关闭"。使用半关闭TCP可以使连接的一端终止其输出,同时仍从另一端接收数据。

TCP协议编程

发送方和接收方的成对的两个socket之间必须建立连接,以便在TCP协议的基础上进行通信。
当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送或接收操作。

搭建服务器和客户端

1、使用ServerSocket创建TCP服务端
ServerSocket:
实现服务器套接字。服务器套接字等待请求通过网络传入。
构造方法:
ServerSocket(int port)//port:端口号
常用方法
accept()//获取一个和客户端类型一致的套接字对象

package 网络编程;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
	public static void main(String[] args) {
	try {
		ServerSocket server =new ServerSocket(9999);
		while(true) {
			Socket client =server.accept();
		    if(client!=null) {
		    	System.out.println("有客户端链接上来了!!!");
		    }
		}
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	}
}

2、使用Socket创建客户端
Socket:
实现客户端套接字。
发送和接收数据采用的是io流技术。
构造方法
Socket(InetAddress address, int port) //address:目标的ip port:端口号

常用方法:
getOutputStream() //获取字节输出流
getInputStream() //获取字节输入流

package 网络编程;

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {
    public static void main(String[] args) {
		try {
			Socket clicent =new Socket("127.0.0.1",9999);
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

服务端与客户端对话

注意:
在TCP协议通信时,客户端和服务器端建立起连接后,
通过Socket套接字的
getInputStream()返回此套接字的输入流,
getOutputStream()返回套接字的输出流。
通信的双方通过输入、输出流读写数据。

服务器端

步骤:
创建ServerSocket套接字对象--> 提供Socket套接字对象
-->获取字节输入流 --> 创建读取内容的字节数组 --> 读取内容
-->解析内容 --> 获取字节输出流 --> 使用输出流写出内容
-->关闭流资源

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {
	public static void main(String[] args) throws IOException {
		ServerSocket server = new ServerSocket(9999);
		while (true) {
			StringBuffer data = new StringBuffer();
			Socket client = server.accept();
			if (client != null) {//如果连接成功
				System.out.println(client + "连接成功");
				
				InputStream is = client.getInputStream();
				OutputStream os = client.getOutputStream();
				// 2 服务器端读数据
				byte[] bytes = new byte[1024];
				int len = 0;
				while ((len = is.read(bytes)) != -1) {
					data.append(new String(bytes, 0, len));
				}

				System.out.println("接受消息成功,消息为:");
				System.out.println(data.toString());
                 //3. 向客户端反馈信息
				String msg = "I have received: " + data.toString();

				os.write(msg.getBytes());

				is.close();
				os.close();
			}
          //客户端发送"quit"字符串,服务器终止程序
			if ("quit".equals(data.toString())) {
				break;
			}
		}
		server.close();
	}
}

客户端

步骤:
创建Socket套接字对象--> 获取字节输出流
--> 创建内容的字节数组 --> 使用输出流写出内容
-->关闭流资源(shutdownOutput)

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class TCPClient {
	public static void main(String[] args) throws UnknownHostException, IOException {
		InetAddress ip12 = InetAddress.getLocalHost();
		Socket clicent = new Socket(ip12, 9999);
		OutputStream os = clicent.getOutputStream();
        //1. 用户输入数据,向服务器发送数据
		Scanner sc = new Scanner(System.in);
		String data = sc.nextLine();
		os.write(data.getBytes());
		/************注意********/
		//注意这里一定要加一个shutdownOutput方法表示客户端写数据结束,要不然程序会把下面的程序当成客户端写入的程序执行
		clicent.shutdownOutput();
		/************注意********/
		// 4 客户端接收数据,显示
		InputStream is = clicent.getInputStream();
		int len = 0;
		byte[] bytes = new byte[10];
		StringBuffer buffer = new StringBuffer();
		while ((len = is.read(bytes)) != -1) {
			buffer.append(new String(bytes, 0, len));
		}
		// 显示
		System.out.println(buffer.toString());
		os.close();
		is.close();
		sc.close();

		clicent.close();
	}
}

UDP协议编程

类似写信
速度快,但是可能会丢失数据
如果接受的字节数组小,会丢失数据

DatagramSocket:

是UDP编程的套接字 ,此类表示用来发送和接收数据报包的套接字。
构造方法:
DatagramSocket()//获取套接字对象【一般用于发送端】
DatagramSocket(int port)//获取套接字对象【一般用于接收端】
常用方法:
send(DatagramPacket dp)//发送数据报包出去
receive(DatagramPacket dp)//接收数据报包

DatagramPacket:

数据报包;指包装了发送的数据和接收数据数据的一个容器。【包括:发送和接收的内容、发送的ip地址、端口号等内容】
常用方法:
getData()//获取数据报包中的内容数组
getLength()//获取数据报包中有效内容的长度

UDP接收端

步骤:
获取套接字
准备接收容器
接收数据到数据报包
解析接收到的数据
准备回应的内容
将内容添加到接收使用的数据报包中
将该数据报包发送回发送端

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPServer {
     public static void main(String[] args) throws IOException {
        DatagramSocket server=new  DatagramSocket(8888);
        
            byte[] bytes=new byte[110];
            //准备接收数据报包
            DatagramPacket dPacket=new DatagramPacket(bytes, bytes.length);
            //接收数据
            server.receive(dPacket);
            byte[] data=dPacket.getData();
            //服务器打印该信息
            System.out.println(new String(data,0,data.length));
            //向客户端发出信息"OK";
            dPacket.setData("OK".getBytes());
            server.send(dPacket);
            //收尾
           server.close();
    }
}


UDP发送端

步骤:
获取套接字对象
准备发送的相关数据
获取数据报包同时封装发送的数据
发送数据报包
准备接收数据的容器
准备一个接收数据的数据报包
接收数据
解析数据

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPClient {
    public static void main(String[] args) throws IOException {
        DatagramSocket client = new DatagramSocket();
        // 准备数据
        byte[] data = "Hello World!".getBytes();
        // 准备数据报包
        DatagramPacket dpPacket = new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 8888);
        // 客户端向服务器端发送数据报包
        client.send(dpPacket);

        byte[] msg = new byte[50];
        // 准备接收数据报包
        DatagramPacket msgPacket = new DatagramPacket(msg, msg.length);
        // 接受数据
        client.receive(msgPacket);
        // 输出接收数据
        System.out.println(new String(msgPacket.getData()));
        client.close();
    }

推荐阅读