Java基础-网络编程
一、计算机网络概述
计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
计算机网络功能:
1.资源共享:
资源共享包括计算机硬件资源、软件资源和数据资源的共享。硬件资源的共享提高了计算机硬件资源的利用率,由于受经济和其他因素的制约,这些硬件资派不可能所有用户都有,所以使用计算机网络不仅可以使用自身的硬件资源,也可共享网络上的资源。软件资源和数据资源的共享可以充分利用已有的信息资派.减少软件开发过程中的劳动,避免大型数据库的重复建设。
2.信息交换:
这是计算机网络最基本的功能.计算机网络中的计算机之间或计算机与终端之间,可以快速可靠地相互传递数据、程序或文件。例如,用户可以在网上传送电子邮件、交换数据,可以实现在商业部门或公司之间进行订单、发票等商业文件安全准确地交换。
3.均衡负荷与分布处理:
对于大型的任务或课题.如果都集中在一台计算机上.负荷太重,这时可以将任务分散到不同的计算机分别完成,或由网络中比较空闲的计算机分担负荷,各个计算机连成网络有利于共同协作进行重大科研课题的开发和研究。利用网络技术还可以将许多小型机或傲型机连成具有高性能的分布式计算机系统,使它具有解决复杂问题的能力,从而大大降低费用。
4.综合信息服务:
计算机网络可以向全社会提供各处经济信息、科研情报、商业信息和咨询服务,如Internet中的www就是如此。
计算机网络分类:
局域网(LAN):局域网是一种在小区域内使用的,由多台计算机组成的网络,覆盖范围通常局限在10 千米范围之内,属于一个单位或部门组建的小范围网。
城域网(MAN):城域网是作用范围在广域网与局域网之间的网络,其网络覆盖范围通常可以延伸到整个城市,借助通信光纤将多个局域网联通公用城市网络形成大型网络,使得不仅局域网内的资源可以共享,局域网之间的资源也可以共享。
广域网(WAN): 广城网是一种远程网,涉及长距离的通信,覆盖范围可以是个国家或多个国家,甚至整个世界。由于广域网地理上的距离可以超过几千千米,所以信息衰减非常严重,这种网络一般要租用专线,通过接口信息处理协议和线路连接起来,构成网状结构,解决寻径问题。
网络通信协议:
通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。
网络通信协议有很多种,目前应用最广泛的是TCP/IP协议(Transmission Control Protocal/Internet Protoal传输控制协议/英特网互联协议),它是一个包括TCP协议和IP协议,UDP(User Datagram Protocol)协议和其它一些协议的协议组,在学习具体协议之前首先了解一下TCP/IP协议组的层次结构。
在进行数据传输时,要求发送的数据与收到的数据完全一样,这时,就需要在原有的数据上添加很多信息,以保证数据在传输过程中数据格式完全一致。TCP/IP协议的层次结构比较简单,共分为四层,如图所示。

图中,TCP/IP协议中的四层分别是应用层、传输层、网络层和链路层,每层分别负责不同的通信功能,接下来针对这四层进行详细地讲解。
- 链路层:链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。
- 网络层:网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。
- 传输层:主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。
- 应用层:主要负责应用程序的协议,例如HTTP协议、FTP协议等。
二、网络通信协议
1.IP地址与端口号:
要想使网络中的计算机能够进行通信,必须为每台计算机指定一个标识号,通过这个标识号来指定接受数据的计算机或者发送数据的计算机。
在TCP/IP协议中,这个标识号就是IP地址,它可以唯一标识一台计算机,目前,IP地址广泛使用的版本是IPv4,它是由4个字节大小的二进制数来表示,如:00001010000000000000000000000001。由于二进制形式表示的IP地址非常不便记忆和处理,因此通常会将IP地址写成十进制的形式,每个字节用一个十进制数字(0-255)表示,数字间用符号“.”分开,如 “192.168.1.100”。
随着计算机网络规模的不断扩大,对IP地址的需求也越来越多,IPV4这种用4个字节表示的IP地址面临枯竭,因此IPv6 便应运而生了,IPv6使用16个字节表示IP地址,它所拥有的地址容量约是IPv4的8×1028倍,达到2128个(算上全零的),这样就解决了网络地址资源数量不够的问题。
通过IP地址可以连接到指定计算机,但如果想访问目标计算机中的某个应用程序,还需要指定端口号。
在计算机中,不同的应用程序是通过端口号区分的。端口号是用两个字节(16位的二进制数)表示的,它的取值范围是0~65535,其中,0~1023之间的端口号用于一些知名的网络服务和应用,用户的普通应用程序需要使用1024以上的端口号,从而避免端口号被另外一个应用或服务所占用。

上图中可以清楚地看到,位于网络中一台计算机可以通过IP地址去访问另一台计算机,并通过端口号访问目标计算机中的某个应用程序。
2.InetAddress类:
InetAddress:类的主要作用是封装IP及DNS,因为这个类没有构造器,所以我们要用他的一些方法来获得对象。
public class InetAddress01 { public static void main(String[] args) throws UnknownHostException { // 使用getLocalHost方法为InetAddress创建对象; InetAddress add=InetAddress.getLocalHost();//获得本机的InetAddress对象 System.out.println(add.getHostAddress());//返回本机IP地址 System.out.println(add.getHostName());//输出计算机名 //根据域名得到InetAddress对象 add=InetAddress.getByName("www.baidu.com"); System.out.println(add.getHostAddress());//返回百度服务器的IP地址 System.out.println(add.getHostName());//输出www.baidu.com; //根据ip得到InetAddress对象; add=InetAddress.getByName("111.13.100.91"); System.out.println(add.getHostAddress()); System.out.println(add.getHostName());//如果ip地址存在,并且DNS给你解析就会输出 //www.baidu.com,不给你解析就会返回这个IP本身; } }
InetSocketAddress类主要作用是封装端口 他是在在InetAddress基础上加端口,但它是有构造器的。
/** * 封装端口 在InetADDress基础上加端口 */ public class InetSocketskh01 { public static void main(String[] args) { InetSocketAddress add=new InetSocketAddress("100.95.227.210",9999); System.out.println(add.getHostName()); System.out.println(add.getPort()); InetAddress addr=add.getAddress();//获得端口的ip; System.out.println(addr.getHostAddress());//返回ip; System.out.println(addr.getHostName());//输出端口名; } }
3.URL统一资源定位符:(网络三大基石:HTTP、HTML、URL)
它表示Internet上某一资源的地址。通过URL我们可以访问Internet上的各种网络资源,比如最常见的www,ftp站点。浏览器通过解析给定的URL可以在网络上查找相应的文件或其他资源。
URI=URL+URN
- URI:Uniform Resource Identifier ,统一资源标志符。
- URL:Uniform Resource Locator,统一资源定位符。
- URN:Uniform Resource Name,统一资源命名。
URL的基本结构由5部分组成:
<传输协议>://<主机名>:<端口号>/<文件名>#片段名?参数列表
片段名:即锚点,例如看小说,直接定位到章节
参数列表格式:参数名=参数值&参数名=参数值......
例如:http://localhost:8080/index.jsp#a?username=Tom&password=123456
URL类:java.net包下
1.构造方法摘要
URL(String spec)根据 String 表示形式创建 URL 对象。
URL(String protocol, String host, int port, String file) 根据指定协议名、主机名、端口号和文件名创建 URL 对象。
URL(String protocol, String host, String file) 根据指定的协议名、主机名和文件名创建 URL。
2.常用方法摘要
String getProtocol()获取此 URL的协议名称。
String getHost() 获取此 URL 的主机名。
int getPort() 获取此 URL 的端口号。
String getPath() 获取此 URL 的文件路径。
String getFile()获取此 URL 的文件名。
String getQuery()获取此 URL的查询部分。
URLConnection openConnection() 返回一个URLConnection实例,表示与URL引用的远程对象的URL 。
URLConnection类中又有一个方法:
InputStream getInputStream() 返回从此打开的连接读取的输入流。
import java.net.MalformedURLException; import java.net.URL; public class Test { public static void main(String[] args) throws MalformedURLException { URL url = new URL("http://localhost:8080/index.jsp?username=Tom&password=123456"); System.out.println(url.getProtocol());//获取协议名 System.out.println(url.getHost());//获取主机名 System.out.println(url.getPort());//获取端口号 System.out.println(url.getPath());//获取文件路径 System.out.println(url.getFile());//获取文件名 System.out.println(url.getQuery());//获取查询名 } }
//网络资源下载 import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; public class Test { public static void main(String[] args) throws IOException { //下载地址 URL url = new URL("https://img.t.sinajs.cn/t6/style/images/global_nav/WB_logo.png?id=1404211047727"); //连接到这个资源 HTTP HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); InputStream is = urlConnection.getInputStream(); FileOutputStream fos = new FileOutputStream("weibo.jpg"); byte[] buffer = new byte[1024]; int len=0; while ((len=is.read(buffer))!=-1){ fos.write(buffer,0,len); } //释放资源 urlConnection.disconnect();//断开连接 is.close(); fos.close(); } }
4.UDP与TCP协议:
java.net包中提供了两种常见的网络协议的支持:TCP和UDP
TCP是可靠的连接,TCP就像打电话,需要先打通对方电话,等待对方有回应后才会跟对方继续说话,也就是一定要确认可以发信息以后才会把信息发出去。TCP上传任何东西都是可靠的,只要两台机器上建立起了连接,在本机上发送的数据就一定能传到对方的机器上。
UDP就好比发电报,发出去就完事了,对方有没有接收到它都不管,所以UDP是不可靠的。
TCP传送数据虽然可靠,但传送得比较慢;UDP传送数据不可靠,但是传送得快。
1).UDP
用户数据报协议(User Datagram Protocol)。
数据报(Datagram):网络传输的基本单位
UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。
由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时,不建议使用UDP协议。
特点:数据被限制在64kb以内,超出这个范围就不能发送了。

2).TCP
传输控制协议(Transmission Control Protocol)。
TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”。
(1)三次握手
TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。

第一次握手,客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。(客户端向服务器端发出连接请求,等待服务器确认。)
第二次握手,服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。(服务器端向客户端回送一个响应,通知客户端收到了连接请求。)
第三次握手,客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。(客户端再次向服务器端发送确认信息,确认连接。)
三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了。
(2)四次挥手:
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

- TCP客户端发送一个FIN,用来关闭客户端到服务端的数据传送,客户端进入FIN_WAIT_1状态。发送报文段内容:FIN=1,seq=u;FIN=1表示请求切断连接;seq=u为客户端请求初始序号。
- 服务端收到这个FIN,它发回一个ACK给客户端,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号;服务端进入CLOSE_WAIT状态。发送报文段内容:ACK=1,seq=v,ack=u+1;ACK=1为确认报文;seq=v为服务器确认初始序号;ack=u+1为客户端初始序号加1。
- 服务器关闭客户端的连接后,发送一个FIN给客户端,服务端进入LAST_ACK状态。发送报文段内容:FIN=1,ACK=1,seq=w,ack=u+1;FIN=1为请求切断连接,ACK=1为确认报文,seq=w为服务端请求切断初始序号。
- 客户端收到FIN后,客户端进入TIME_WAIT状态,接着发回一个ACK报文给服务端确认,并将确认序号设置为收到序号加1,服务端进入CLOSED状态,完成四次挥手。发送报文内容:ACK=1,seq=u+1,ack=w+1;ACK=1为确认报文,seq=u+1为客户端初始序号加1,ack=w+1为服务器初始序号加1。
注意:为什么连接的时候是三次握手,关闭的时候却是四次挥手?
因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭socket,所以只能先回复一个ACK报文,告诉客户端,“你发的FIN报文,我收到了”。只有等到服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送,故需要四步挥手。
三、TCP网络编程
TCP通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)与服务端(Server)。
(1)两端通信时步骤:
服务端程序,需要事先启动,等待客户端的连接。
客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连接客户端。
(2)在Java中,提供了两个类用于实现TCP通信程序:
客户端:java.net.Socket 类表示。创建Socket对象,向服务端发出连接请求,服务端响应请求,两者建立连接开始通信。
服务端:java.net.ServerSocket 类表示。创建ServerSocket对象,相当于开启一个服务,并等待客户端的连接。
Socket 类:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点。
(1)构造方法摘要
public Socket(String host, int port) :创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的host是null ,则相当于指定地址为回送地址。
回送地址(127.x.x.x) 是本机回送地址(Loopback Address),主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,立即返回,不进行任何网络传输。
例如:Socket client = new Socket("127.0.0.1", 6666);
(2)常用方法摘要
public InputStream getInputStream() : 返回此套接字的输入流。
如果此Scoket具有相关联的通道,则生成的InputStream 的所有操作也关联该通道。
关闭生成的InputStream也将关闭相关的Socket。
public OutputStream getOutputStream() : 返回此套接字的输出流。
如果此Socket具有相关联的通道,则生成的OutputStream 的所有操作也关联该通道。
关闭生成的OutputStream也将关闭相关的Socket。
public void close() :关闭此套接字。
一旦一个socket被关闭,它不可再使用。
关闭此socket也将关闭相关的InputStream和OutputStream 。
public void shutdownOutput() : 禁用此套接字的输出流。
任何先前写出的数据将被发送,随后终止输出流。
ServerSocket类:这个类实现了服务器套接字,该对象等待通过网络的请求。
(1)构造方法摘要
public ServerSocket(int port) :使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上,参数port就是端口号。
例如:ServerSocket server = new ServerSocket(6666);
(2)常用方法摘要
public Socket accept() :侦听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法会一直阻塞直到建立连接。
//客户端 import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; public class TCPClient { public static void main(String[] args){ Socket socket = null; OutputStream os = null; try { //1、创建Socket对象,它的第一个参数需要的是服务端的IP,第二个参数是服务端的端口 InetAddress inet = InetAddress.getByName("127.0.0.1"); socket = new Socket(inet,8090); //2、获取一个输出流,用于写出要发送的数据 os = socket.getOutputStream(); //3、写出数据 os.write("你好,我是客户端!".getBytes()); } catch (IOException e) { e.printStackTrace(); } finally {//4、释放资源 if(socket!=null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if(os!=null){ try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } } } //服务端 import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; public class TCPServer { public static void main(String[] args) { ServerSocket serverSocket = null; Socket socket = null; InputStream is = null; ByteArrayOutputStream baos = null; try { //1、创建服务端的ServerSocket,指明自己的端口号 serverSocket = new ServerSocket(8090); //2、调用accept接收到来自于客户端的socket socket = serverSocket.accept();//阻塞式监听,会一直等待客户端的接入 //3、获取socket的输入流 is = socket.getInputStream(); // 不建议这样写:因为如果我们发送的数据有汉字,用String的方式输出可能会截取汉字,产生乱码 // int len=0; // byte[] buffer = new byte[1024]; // while ((len=is.read(buffer))!=-1){ // String str = new String(buffer, 0, len); // System.out.println(str); // } //4、读取输入流中的数据 //ByteArrayOutputStream的好处是它可以根据数据的大小自动扩充 baos = new ByteArrayOutputStream(); int len=0; byte[] buffer = new byte[1024]; while ((len=is.read(buffer))!=-1){ baos.write(buffer,0,len); } System.out.println("收到了来自于客户端"+socket.getInetAddress().getHostName() +"的消息:"+baos.toString()); } catch (IOException e) { e.printStackTrace(); } finally {//5、关闭资源 if(serverSocket!=null){ try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } if(socket!=null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if(is!=null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if(baos!=null){ try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
四、UDP网络编程
从技术意义上来讲,只有TCP才会分Server和Client。对于UDP来说,严格意义上,并没有所谓的Server和Client。
java.net包给我们提供了两个类DatagramSocket(此类表示用于发送和接收数据报的套接字)和DatagramPacket(该类表示数据报的数据包。 )
1.DatagramSocket
(1)构造方法摘要
protected DatagramSocket()构造数据报套接字并将其绑定到本地主机上的任何可用端口。
protected DatagramSocket(int port)构造数据报套接字并将其绑定到本地主机上的指定端口。
protected DatagramSocket(int port, InetAddress laddr)创建一个数据报套接字,绑定到指定的本地地址。
2.DatagramPacket
(1)构造方法摘要
DatagramPacket(byte[] buf, int offset, int length)构造一个 DatagramPacket用于接收指定长度的数据报包到缓冲区中。
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)构造用于发送指定长度的数据报包到指定主机的指定端口号上。
(2)常用方法摘要
byte[] getData() 返回数据报包中的数据。
InetAddress getAddress() 返回该数据报发送或接收数据报的计算机的IP地址。
int getLength() 返回要发送的数据的长度或接收到的数据的长度。
//发送方 import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class UDPSender { public static void main(String[] args) throws IOException { //1、创建一个socket DatagramSocket socket = new DatagramSocket(); InetAddress inet = InetAddress.getLocalHost(); String msg="你好,很高兴认识你!"; byte[] buffer = msg.getBytes(); //2、创建一个包(要发送给谁) DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length,inet,9090); //3、发送包 socket.send(packet); //4、释放资源 socket.close(); } } //接收方 import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; public class UDPReceiver { public static void main(String[] args) throws IOException { //1、创建一个socket,开放端口 DatagramSocket socket = new DatagramSocket(9090); byte[] buffer = new byte[1024]; //2、创建一个包接收数据 DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length); //3、接收数据 socket.receive(packet);//阻塞式接收 //将数据包转换为字符串输出 String msg = new String(packet.getData(), 0, packet.getLength()); System.out.println(msg); //4、释放资源 socket.close(); } }
浙公网安备 33010602011771号