Java-网络编程初识
网络基础
计算机网络: 把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规 模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息、 共享硬件、软件、数据信息等资源。
网络编程的目的:
- 直接或间接地通过网络协议与其它计算机实现数据交换,进行通讯。
网络编程中有两个主要的问题:
- 如何准确地定位网络上一台或多台主机;定位主机上的特定的应用
- (IP和端口号)
- 找到主机后如何可靠高效地进行数据传输
- (提供网络通信协议)
如何实现网络中的主机互相通信?
- 通信双方地址IP
- 端口号
一定的规则
(即:网络通信协议。有两套参考模型)
- OSI参考模型:模型过于理想化,未能在因特网上进行广泛推广
- TCP/IP参考模型(或TCP/IP协议):事实上的国际标准。
IP地址(InetAddress类)
本地回环地址(hostAddress):127.0.0.1 主机名(hostName):localhost(InetAddress.getLocalhost)
IP地址分类方式1:IPV4 和 IPV6
- IPV4:4个字节组成,4个0-255。大概42亿,30亿都在北美,亚洲4亿。2011年初已 经用尽。以点分十进制表示,如192.168.0.1
- IPV6:128位(16个字节),写成8个无符号整数,每个整数用四个十六进制位表示, 数之间用冒号(:)分开,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984
IP地址分类方式2:公网地址(万维网使用)和私有地址(局域网使用)。192.168. 开头的就是私有址址,范围即为192.168.0.0--192.168.255.255,专门为组织机构内部使用
package www.exer.collection; import java.net.InetAddress; import java.net.UnknownHostException; public class IPTest { public static void main(String[] args) { try { InetAddress address = InetAddress.getByName("192.168.10.14"); System.out.println(address); } catch (UnknownHostException e) { e.printStackTrace(); } } }
Internet上的主机有两种方式表示地址:
域名(hostName):www.atguigu.com
IP 地址(hostAddress):202.108.35.210
InetAddress类主要表示IP地址,两个子类:Inet4Address、Inet6Address。
InetAddress 类 对 象 含 有 一 个 Internet 主 机 地 址 的 域 名 和 IP 地 址 :
package www.exer.collection; import java.net.InetAddress; import java.net.UnknownHostException; public class IPTest { public static void main(String[] args) { try { InetAddress address = InetAddress.getByName("192.168.10.14"); System.out.println(address); InetAddress address1 = InetAddress.getByName("www.bravovax.com"); System.out.println(address1); } catch (UnknownHostException e) { e.printStackTrace(); } } }
输出结果:
/192.168.10.14
www.bravovax.com/211.149.140.76
域名容易记忆,当在连接网络时输入一个主机的域名后,域名服务器(DNS) 负责将域名转化成IP地址,这样才能和主机建立连接。 -------域名解析
端口号标识正在计算机上运行的进程(程序)
不同的进程有不同的端口号
被规定为一个 16 位的整数 0~65535。
端口分类:
公认端口:0~1023。被预先定义的服务通信占用(如:HTTP占用端口 80,FTP占用端口21,Telnet占用端口23)
注册端口:1024~49151。分配给用户进程或应用程序。(如:Tomcat占 用端口8080,MySQL占用端口3306,Oracle占用端口1521等)。
动态/私有端口:49152~65535。
端口号与IP地址的组合得出一个网络套接字:Socket。
Socket
利用套接字(Socket)开发网络应用程序早已被广泛的采用,以至于成为事实
上的标准。
网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标
识符套接字。
通信的两端都要有Socket,是两台机器间通信的端点。
网络通信其实就是Socket间的通信。
Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。
一般主动发起通信的应用程序属客户端,等待通信请求的为服务端。
Socket分类:
流套接字(stream socket):使用TCP提供可依赖的字节流服务
数据报套接字(datagram socket):使用UDP提供“尽力而为”的数据报服务
实现TCP的网络编程
客户端Socket的工作过程包含以下四个基本的步骤:
①创建 Socket:根据指定服务端的 IP 地址或端口号构造 Socket 类对象。若服务器端
响应,则建立客户端到服务器的通信线路。若连接失败,会出现异常。
②打开连接到 Socket 的输入/出流: 使用 getInputStream()方法获得输入流,使用
getOutputStream()方法获得输出流,进行数据传输
③按照一定的协议对 Socket 进行读/写操作:通过输入流读取服务器放入线路的信息
(但不能读取自己放入线路的信息),通过输出流将信息写入线程。
④关闭 Socket:断开客户端到服务器的连接,释放线路
【示例一:客户端发送和信息给服务端,服务端将数据显示在控制台上】
1 package www.exer.collection; 2 3 import com.sun.security.ntlm.Server; 4 import org.junit.Test; 5 6 import java.io.ByteArrayOutputStream; 7 import java.io.IOException; 8 import java.io.InputStream; 9 import java.io.OutputStream; 10 import java.net.InetAddress; 11 import java.net.ServerSocket; 12 import java.net.Socket; 13 14 import static org.junit.Assert.*; 15 /** 16 * @Name 17 * @Description 客户端发送和信息给服务端,服务端将数据显示在控制台上 18 * @Author Fancy 19 * @return 2022/3/13 20 * @Version 1.0 21 */ 22 public class IPTestTest { 23 24 //客户端 25 @Test 26 public void client(){ 27 Socket socket = null; 28 OutputStream os = null; 29 try { 30 //指明对方的IP地址 31 InetAddress inet = InetAddress.getByName("192.168.101.19"); 32 //和服务器上的33088端口号通信 33 socket = new Socket(inet,33088); 34 //字节流、输出流 35 os = socket.getOutputStream(); 36 //输出了hello!这条消息到服务器 37 os.write("hello!".getBytes()); 38 } catch (IOException e) { 39 e.printStackTrace(); 40 } finally { 41 if(socket != null); 42 try { 43 //socket也是资源,也需要关闭 44 socket.close(); 45 } catch (IOException e) { 46 e.printStackTrace(); 47 } 48 if(os != null); 49 try { 50 os.close(); 51 } catch (IOException e) { 52 e.printStackTrace(); 53 } 54 } 55 } 56 57 //服务端(先启动) 58 @Test 59 public void server(){ 60 ServerSocket ss = null; 61 Socket socket = null; 62 InputStream is = null; 63 ByteArrayOutputStream baos= null; 64 try { 65 //1.创建服务器的seversocket,指明自己的端口号 66 ss = new ServerSocket(33088); 67 //2.接收来自客户端的socket 68 socket = ss.accept(); 69 //3.获取输入流,用于接收来自客户端的数据 70 is = socket.getInputStream(); 71 //不建议这样写,可能会有乱码 72 /* byte[] buffer = new byte[20]; 73 int len; 74 while ((len= is.read(buffer)) != -1){ 75 String str = new String(buffer,0,len); 76 System.out.println(str); 77 }*/ 78 //避免了中文字符的byte转换时被截断 79 //4.通信:读取输入流中的数据 80 baos = new ByteArrayOutputStream(); 81 byte[] buffer = new byte[5]; 82 int len; 83 while((len = is.read(buffer)) != -1){ 84 baos.write(buffer,0,len); 85 } 86 System.out.println(baos.toString()); 87 } catch (IOException e) { 88 e.printStackTrace(); 89 } finally { 90 try { 91 if (ss != null) 92 ss.close(); 93 } catch (IOException e) { 94 e.printStackTrace(); 95 } 96 try { 97 if (is != null) 98 is.close(); 99 } catch (IOException e) { 100 e.printStackTrace(); 101 } 102 try { 103 if (socket != null) 104 socket.close(); 105 } catch (IOException e) { 106 e.printStackTrace(); 107 } 108 try { 109 if (baos != null) 110 baos.close(); 111 } catch (IOException e) { 112 e.printStackTrace(); 113 } 114 } 115 } 116 }
- 先启动服务器,创建一个服务器端套接字,并绑定到指定端口上,用于监听客户端的请求
- 调用accept,监听连接请求,如果客户端请求连接,则接受并返回通信套接字对象。
- 调用该Socket类对象的 getOutputStream() 和 getInputStream ():获取输出 流和输入流,开始网络数据的发送和接收。
- 闭ServerSocket和Socket对象:客户端访问结束,关闭通信套接字。
【示例二:客户端发送文件给服务端,服务端将文件保存在本地】
package www.exer.collection; import org.junit.Test; import java.io.*; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; /** * @Name * @Description 客户端发送文件给服务端,服务端将文件保存在本地 * @Author Fancy * @return 2022/3/13 * @Version 1.0 */ public class TCPTest2 { @Test public void client() throws IOException { //指定服务器的IP地址 InetAddress inet1 = InetAddress.getByName("192.168.101.19"); //准备socket,连接服务器,指定服务器的IP地址和端口号 Socket soc = new Socket(inet1,22222); //创建要传输的文件对象 File file = new File("D:\\整理\\Exer_code\\src\\www\\hello.txt"); //获取输出流,用来发送数据给服务器 OutputStream output = soc.getOutputStream(); //发送文件 //创建文件流 BufferedInputStream fileIS = new BufferedInputStream(new FileInputStream(file)); //将文件读入 byte[] bis = new byte[10]; int len; while((len = fileIS.read(bis)) != -1){ output.write(bis,0,len); } //在输出流的末尾写入一个“流的末尾”标记 output.flush(); soc.shutdownInput(); fileIS.close(); fileIS.close(); soc.close(); output.close(); } @Test public void server() throws IOException { //1.准备一个ServerSocket ServerSocket server = new ServerSocket(22222); //2.监听一个客户端的连接 Socket socket = server.accept();//该方法是一个阻塞的方法,会一直等待客户端连接 //3.获取输入流,接收客户端数据 InputStream input = socket.getInputStream(); //创建文件流 BufferedOutputStream fileOS = new BufferedOutputStream(new FileOutputStream ("D:\\整理\\Exer_code\\src\\www\\hello1.txt")); //4.通信,接收数据 byte[] bos = new byte[10]; int len; while ((len = input.read(bos)) != -1){ //将接收的数据写入文件流 fileOS.write(bos,0,len); } //5.关闭资源 server.close(); input.close(); fileOS.close(); input.close(); } }
以上实例中服务端除了可以自定义,还有Tomcat服务器