本文共 15637 字,大约阅读时间需要 52 分钟。
计算机网络是通过传输介质、通信设施、和网络通信,把分散在不同地点的计算机设备互连起来,实现数据传输和数据共享的系统。网络编程就是通过程序使互联网中的两个或多个设备之间进行数据传输。
通过计算机网络可以使多台计算机实现连接,但是位于同一个网络中的计算机在进行连接和通信时必须要遵守一定的规则,这就好比在道路中行驶的汽车要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议。它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交互。
网络通信协议有很多种,目前应用最广泛的有TCP/IP协议
(Transmission Control Protocol/Internet Protocol,传输控制协议/英特网互联协议)、UDP协议
(User Datagram Protocol,用户数据报协议)和其他一些协议的协议组。 为了减少网络编程设计的复杂性,绝大多数网络采用分层设计方法。所谓分层设计,就是按照信息的流动过程将网络的整体功能分解为一个个的功能层,不同机器上的同等功能层之间采用相同的协议,同一机器上的相邻功能层之间通过接口进行信息传递。
TCP/IP协议(又称为TCP/IP协议旗)是一组用于实现网络互联的信协议,其名称来源于该协议演中的两个重要协议TCP协议和IP协议,基于TCP/IP协议参考模型的网络层次结构比较简单,共分为四层。 TCP/IP协议的四层结构:
链路层:针对光纤和双绞线提供的驱动,物理设备。用于定义物理传输通道 * 物理驱动 、 接口 网络层:是TCP/IP协议的核心,主要用于将数据进行分组,再将分组的数据发送到目标计算机或者网络 * IP ICMP IGMP 运输层:使用网络程序进行通信,可采用TCP协议,也可以用UDP协议 * UDP协议属于无连接通信协议 - 消耗资源少、通信效率高、延迟小 - 不能保证数据的完整性,会导致部分数据包丢失 - 常用与视频会议 - 常用类: DatagramSocket 接收端 DatagramPacket 发送端 * TCP协议是面向连接的通信协议,在发送与接收端建立逻辑连接后发送数据,保证了计算机之间可靠无差错的数据传输 - 速度慢、数据可靠性高 - TCP每次连接的创建都要“三次握手”,必须要明确客户端与服务器端 客户端发送请求 服务器端响应 客户端确认连接 - “四次挥手” 客户端传输完数据请求断开 服务器确认客户端断开 服务器响应完毕请求断开 客户端确认服务器端断开 - 常用类: serverSocket 接收端 Socket 发送端 应用层:主要为网络中的各种网络应用提供服务。 * HTTP FTP DNS
要想使网络中的计算机能够进行通信,还必须为每台计算机指定一个标识号,通过这个原识号来指定接收数据的计算机或者发送数据的计算机。在TCP/IP协议中,这个标识号就是IP地址,它可以唯一标识一台计算机
,目前,IP地址广泛使用的版本是IPv4,它是由个字节大小的二进制数来表示,如000010100000000000000000000001。由于二进制形式表示的IP地址非常不便于记忆和处理,因此通常会将IP地址写成十进制的形式,每个字节用一个十进制数字(0~255)表示,数字间用符号.分开表示4段数字,如10.0.0.1.
随着计算机网络规模的不断扩大,对IP地址的需求也越来越多,1Pv4这种用4个字节表示的IP地址面临枯竭(实际情况是在2011年2月3日IPv4位地址已经分配完毕),因此IPv6版本的IP地址便应运而生了。IPv6使用16个字节表示IP地址,它所拥有的地址容量约是IPv4的8×1028倍,达到2128个(算上全零的),这样就解决了网络地址资源数量不足的问题。
最初设计互联网时,为了便于寻址以及层次化构造网络,每个IP地址由两部分组成,即网络、主机
”的形式,其中网络部分表示其属于互联网的哪一个网络,是网络的地址编码,主机部分表示其属于该网络中的哪一台主机,是网络中一个主机的地址编码,二者是主从关系。IP地址总共分为5类,常用的有A、B、C三类(另外的D和E类为特殊地址),介绍如下。
1.0.0.0
到127.255.255.255
。 ②B类地址:由前两段的网络地址和其余两段的主机地址组成,范围是128.0.0.0
到191.255.255.255
。 ③C类地址:由前三段的网络地址和最后一段的主机地址组成,范围是192.0.0.0
到23.255.255.255
。 另外,还有一个本地回环地址127.0.0.1,指本机地址,该地址一般用来测试使用,例加:用ping127.0.0.1来测试本机TCP/IP协议是否正常
。 通过IP地址可以连接到指定计算机,但如果想访问目标计算机中的某个应用程序,还需要指定端口号。在计算机中,不同的应用程序是通过端口号区分的,端口号是用两个字节(16位的二进制数)表示的,它的取值范围是0-65535.其中,0~1023之间的端口号用于一些知名的网络服务和应用,用户的普通应用程序需要使用1024以上的端口号,从而避免端口号被另外一些应用或服务质占用。
InetAddress类无构造方法
常用方法摘要:
byte[] getAddress()
返回此 InetAddress 对象的原始 IP 地址。 static InetAddress getByName(String host)
在给定主机名的情况下确定主机的 IP 地址。 String getHostAddress()
返回 IP 地址字符串(以文本表现形式)。 String getHostName()
获取此 IP 地址的主机名。 static InetAddress getLocalHost()
返回本地主机。 import java.net.InetAddress;public class InetAddressTest { public static void main(String[] args) { try { // 获取本地主机的InetAddress对象 InetAddress localAddress = InetAddress.getLocalHost(); System.out.println(localAddress); // 直接打印InetAddress对象 // 获取字符串格式的原始IP地址 System.out.println("本机的IP地址是:" + localAddress.getHostAddress()); // 获取给定主机名的InetAddress对象 InetAddress baiDuAddress = InetAddress.getByName("www.baidu.com"); System.out.println("百度的IP地址是:" + baiDuAddress.getHostAddress()); // 判断限定时间内是否可以访问指定的IP地址 System.out.println("5秒内是否可以访问百度网站:" + baiDuAddress.isReachable(500)); // 获取本地IP地址的主机名 System.out.println("baidu的主机名为:" + baiDuAddress.getHostName()); System.out.println("本机的主机名为:" + localAddress.getHostName()); } catch (Exception e) { e.printStackTrace(); } }}
在介绍TCP/IP结构时,提到传输层两个重要的高级协议,分别是UDP和TCP:
UDP是User Datagram Protocol的简称,称为
TCP是Transmission Control Protocol的简称,称为用户数据报协议
;传输控制协议
。
UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接
。
UDP协议消耗资源小、通信效率高、延迟小
,所以通常都会用于音频、视频和普通数据的传输,例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性
,因此在传输重要数据时不建议使用UDP协议。 TCP协议是面向连接的通信协议,即在传输数据前先在发送端和接收端建立逻辑连接,然后再传输数据
,它保证了两台计算机之间可靠无差错的数据传输
,在TCP连度中必须要明确客户端与服务器端,先由客户端向服务端发出连接请求,每次连接的创建都需要过“三次握手”。
第一次据手,客户端向服务器端发出连接请求,等待服务器确认:
第二次手、服务器端向客户端返回一个响应.通知客户编收到了连接请求: 第三次握手,客户编再次向服务器端发送确认信息,确认连接。
所以,TCP协议传送速度较慢
,但传送的数据比较可靠
。由于TCP协议的面向连接特性,它可以保证传输数据的安全性和完整性,所以是一个被广泛采用的协议,例如在下载文件时,如果数据接收不完整,将会导致文件数据丢失而不能被打开,因此,下载文件时必须采用TCP协议。
UDP是一种面向无连接的协议,因此,在通信时发送端和接收端不用建立连接。UDP通信的过程就像是货运公司在两个码头间发送货物一样,在码头发送和接收货物时都需要使用集装箱来装载货物。UDP通信也是一样,发送和接收的数据也需要使用“集装箱”进行打包。
为此JDK中提供了一个DatagramPacket类,该类的实例对象就相当于一个集装箱,用于封装 UDP 通信中发送或者接收的数据
。然而运输货物只有“集装箱”是不够的,还需要有码头。为此,JDK提供了与之对应的DatagramSocket类,该类的作用就类似于码头,用这个类的实例对象就可以发送和接收DatagramPacket数据报.
DatagramPacket用于封装UDP通信中的数据。在创建发送端和接收端的DatagramPacket对象时,使用的构造方法有所不同,接收端的构造方法只需要接收一个字结数组来存放接收到的数据,而发送端的构造方法不但要接收存放了发送数据的字节数组,还需要指定发送端IP地址和端口号。接下来根据API文档,对DatagramPacket类常用的构造方法进行讲解。
(1)DatagramPacket(byte[]buf,int length)
使用该构造方法在创建DatagramPacket对象时,指定了封装数据的字节数组和数据的大小,没有指定IP地址和端口号。很明显,这样的对象只能用于接收端,不能用于发送端。因为发送端一定要明确指出数据的目的地(IP地址和端口号),而接收端不需要明确知道数据的来源,只需要接收到数据即可。(2)DatagramPacket(byte[]buf,int offset,int length)。
该构造方法与第一个构造方法类似,同样用于接收端,只不过在第一个构造方法的基础上,增加了一个offset参数,该参数用于指定一个数组中发送数据的偏移量为offset,即从ffset位置开始发送数据。
(3)DatagramPacket(byte[]buf,int length,InetAddress addr,int port)。
使用该构造方法在创建DatagramPacket对象时,不仅指定了封装数据的字节数组和数据的大小,还指定了数据包的目标IP地址(addr)和端口号(port)。该对象通常用于发送端,因为在发送数据时必须指定接收端的IP地址和端口号,就好像发送货物的集装箱上面必须标明接收人的地址一样(4)DatagramPacket(byte[]buf,int offset,int length,InetAddress addr,int port)。
该构造方法与第三个构造方法类似,同样用于发送端,只不过在第三个构造方法的基础上,增加了一个offset参数,该参数用于指定一个数组中发送数据的偏移量为offset,即从ffset位置开始发送数据。DatagramSocket 用于创建发送端和接收端对象,然而在创建发送增和接收理的DatagramSocket对象时,使用的构造方法有所不同,下面对DatagramSocket类中常用的构造方法进行讲解。
(1)DatagramSocket().
该构造方法用于创建发送端的DatagramSocket对象,在创建DatagramSocket对象时,开没有指定端口号,此时,系统会分配一个没有被其他网络程序所使用的端口号。
(2)DatagramSocket(int port).
该构造方法既可用于创建接收端的DatagramSocket对象,也可以创建发送端的DatugramSocket对象,在创建接收端的DatagramSocket对象时,必须要指定一个项口号.这样就可以监听指定的端口。
(3)DatagrnmSocket(int port,InetAddress addr).
使用该构造方法在创建DatagramSocket时,不仅指定了端口号还指定了相关的IP址,这种情况适用于计算机上有多块网卡的情况,可以明确规定数据通过哪块网卡向外发和接收哪块网卡的数据,由于计算机中针对不同的网卡会分配不同的IP、因此在创DatagramSocket对象时需要通过指定IP地址来确定使用哪块网卡进行通信。发送端:
import java.net.DatagramSocket;import java.net.InetAddress;public class UDPClient { public static void main(String[] args) { DatagramSocket client = null; DatagramPacket datagramPacket =null; try { // 1.创建发送端,指定端口 client = new DatagramSocket(7777); // 2.创建要传输的信息 String str = "Hello World"; // 3.创建datagramPacket,封装将要发送的信息,指明数据报的目标IP地址以及端口 datagramPacket = new DatagramPacket(str.getBytes(),str.length(), InetAddress.getByName("localhost"),8888); System.out.println("开始发送信息..."); // 4.通过发送端开始发送信息 client.send(datagramPacket); } catch (Exception e){ e.printStackTrace(); } finally { if (client!=null){ // 关闭释放资源 client.close(); } } }}
接收端:
import java.net.DatagramSocket;public class UDPServer { public static void main(String[] args) { DatagramSocket receiver = null; DatagramPacket datagramPacket = null; try{ // 1.创建接收对象,指定端口 receiver = new DatagramSocket(8888); // 2.定义字节缓存 byte[] buf = new byte[1024]; // 3.创建数据报,封装字节缓存内的数据,指定了封装数据的字节数组和数据的大小 datagramPacket = new DatagramPacket(buf,buf.length); System.out.println("接收端等待接收数据..."); // 4.等待接收 while(true){ // 等待接收数据,若没有接收到数据将处于阻塞状态 receiver.receive(datagramPacket); //将要接受的数据转为字符串类型 String str = new String(datagramPacket.getData(),0,datagramPacket.getLength()); System.out.println(datagramPacket.getAddress()+":"+datagramPacket.getPort()+"发送的信息:"+str); } } catch (Exception e){ e.printStackTrace(); } }}
1.通过UDP程序发送信息时DatagramSocket既可以充当接受者,也可以充当发送者。 关键取决与当前程序中的DatagramPacket数据报对象创建时参数是否有目标IP和端口的传入。 2.在创建信息发送端是的主要流程: 首先需要创建出DatagramSocket发送对象,指定出端口 其次要指明处发送的消息内容, 然后用DatagramPacket将消息封装成数据报对象, 同时指定目标IP地址及端口 最后,千万不能忘记要对封装好的信息进行发送,并且释放资源 3.创建信息接收端的主要流程: 首先创建出DatagramSocket接收对象,指定出端口 其次要定义字节缓存缓存信息 然后使用DatagramPacket将信息封装为数据报对象 最后将封装好的信息进行接收4.发送(send)、接收(receive)的主要区别在于信息的初步处理,一个是定义,另一个是读取,无论发送还是接收最后都要封装成数据报队形进行处理。5.发送或接收端都可以通过getAddress()、getPort()、getData()、getLength()方法来获取数据的相关信息。6.在创建发送端的时候可以指明端口,系统会自动分配可用的端口,但是在接收端使用getPort()方法时返回值很可能会不一样。
public class Sender implements Runnable { int targetPort; DatagramSocket client = null; DatagramPacket packet = null; public Sender(int targetPort, DatagramSocket client) { this.targetPort = targetPort; this.client = client; } @Override public void run() { Scanner input = new Scanner(System.in); try { while (true){ // 控制台发送信息 String context = input.nextLine(); byte[] buf = context.getBytes(); // 将信息封装为数据报对象,指定目标IP及端口 packet = new DatagramPacket(buf, buf.length, InetAddress.getByName("127.0.0.255"), targetPort); // 发送信息 client.send(packet); } } catch (Exception e) { e.printStackTrace(); } }}
public class Receiver implements Runnable { DatagramSocket server = null; DatagramPacket packet = null; public Receiver(DatagramSocket server) { this.server = server; } @Override public void run() { try{ byte[] buf = new byte[1024]; packet = new DatagramPacket(buf,buf.length); while(true){ // 得到接收的信息 server.receive(packet); // 将得到的信息转化为字符串 String str = new String(packet.getData(),0,packet.getLength()); // 输出信息 System.out.println(packet.getAddress()+":"+packet.getPort()+"发来了信息:"+str); } } catch (Exception e){ e.printStackTrace(); } finally { // 关闭资源 if (server!=null){ server.close(); } } }}
public class QQRoom { public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.print("请输入聊天服务当前启动窗口号:"); int server = input.nextInt(); System.out.print("请输入聊天服务发送信息对象的目标窗口号:"); int receiver = input.nextInt(); System.out.println("聊天系统初始化完成并启动!"); try{ DatagramSocket socket = new DatagramSocket(server); new Thread(new Receiver(socket),"接收信息").start(); new Thread(new Sender(receiver,socket),"发送信息").start(); } catch(Exception e){ e.printStackTrace(); } }}
TCP 通信同UDP通信一样,都能实现两台计算机之间的通信,通信的两端都需要的建Socket对象。TCP通信与UDP通信的其中一个主要区别在于,UDP中只有发送端和接收端,不区分客户端与服务器端,计算机之间可以任意地发送数据。而TCP通信是严格以分客户端与服务器端的,在通信时,必须先由客户端去连接服务器端才能实现通信,服务为端不可以主动连接客户端
。
ServerSocket类,用于表示服务器端
:另一个是Socket类,用于表示客户端
。通信时,首先要创建代表服务器的ServerSocket对象,该对象相当于开启一个服务,并等待客户端的连接;然后创建代表客户端的Socket对象,并向服务器端发出连接请求,服务器端响应请求,两者建立连接后可以正式进行通信。 在开发TCP程序时,首先需要创建服务器端程序。JDK的java.net包中提供了一个ServerSocket类,该类的实例对象可以实现一个服务器端的程序。通过查阅API文档可知,ServerSocket 类提供了多个构造方法,接下来就对ServerSocket的构造方法进行详细讲解
(1)ServerSocket()。
使用该构造方法在创建ServerSocket对象时并没有指定端口号,因此该对象不监听任可端口,不能直接使用,使用时还需要调用bind(SocketAddress endpoint)方法将其绑定到定的端口号上
。 (2)ServerSocket(int port)。
使用该构造方法在创建ServerSocket对象时,可以将其绑定到指定的端口号上。如果 接端指定一个端口号(3)ServerSocket(int port,int backlog)
该构造方法就是在第2个构造方法的基础上,增加了一个baklog参数,该参数用于超定在服务器忙时,可以与之保持连接请求的等待客户端数量,如果没有指定这个参数,默认值认为是50
. (4) ServerSocket(int port. int backlog, InetAddress lindAddr).
该构造方法就是在第3个构造方法的基上,指定了相关的1P地址,这种情况适用于计算机上有多块网卡和多个IP的情况,使用时可以明确规定ServerSocket在哪块网卡或IP地址上等待客户端的连接请求。显然,对于一般只有一块网卡的情况,就不用专门指定发参数。在以上介绍的构造方法中,第2个构造方法是最常使用的。
Socket accept()
该方法用于等待客户端的连接,在客户端连接之前一直处于阻塞状态,如果有客户端连接就会返回一个与之对应的Socket对象
InetAddress getInctAddress()
该方法用于返回一个InetAddress对象,该对象中封装了ServerSocket绑定的IP地址
yoolean isClosed()
该方法用于判断ServerSocket对象是否为关闭状态,如果是关闭状态则返回true,反之则返回false
void bind(SocketAddress endpoint )
该方法用于将ServerSocket对象绑定到指定的IP地址和端口号,其中endpoint封装了IP地址和端口号
ServerSocket对象负责监听某台客户端计算机的端口号,在创建ServerSocket对象后,需要继续调用该对象的accept()方法,接收来自客户端的请求。当执行了accept()方法之后,服务器端程序会发生阻塞,直到客户端发出连接请求,accept()方法才会返回一个Scoket对象用于和客户端实现通信,程序才能继续向下执行。
ServerSocket对象可以实现服务端程序,但只实现服务器端程序还不能完成通信,此时还需要一个客户端程序与之交互,为此JDK提供了一个Socket类,用于实现TCP客户端程序
。通过查阅API文档可知Socket类同样提供了多个构造方法、接下来就对Socket的常用构造方法进行详细讲解。
(1)Socket()。
使用该构造方法在创建Socket对象时,并没有指定IP地址和端口号,也就意味着只创建了客户端对象,并没有去连接任何服务器。通过该构造方法创建对象后还需调用connect(SocketAddress endpoint)方法,才能完成与指定服务器端的连接,其中参数endpoint用于封装IP地址和端口号
。 (2) Socket(String host,int port)
使用该构造 方法在创建 Sorket对象时,会根据参数去连接在指定地址和端口上运行的服务程序,其中参数host接收的是一个字符串类型的IP地址。(3) Socket(InetAddress address,int port)
该方法在使用上与第2个构造方法类似、参数 address 用于接收一个 Iner Address 的对象,该对象用于封装一个IP地址int get Port()
该方法用于返回此Socket连接的远程服务期的日号
actAddress getlool Address()
该方法用于获取Socket对象绑定的本地IP地址,并将IP地址封装成InetAddress类型的对象返回
void close ( )
关闭Socket连接,结束本次通信。在关闭Socket前,应将与Socket相关的所有的输入输出流全部关闭,这是因为一个良好的程序应该在执行完毕时释放所有的资
InputStream getInputStream()
该方法返回一个InputStream类型的输入流对象。如果该对象是由服务器端的Socket返回,就用于该取客户端发送的数据;反之,用于读取服务器端发送的数据
InputStroam getlnputStream()
该方法返回一个OutputStream类型的输出流对象。如果该对象是
Output Stream getOutputStream() 由服务器端的 Socket返回,就用于向客户端发送数据;反之,用于向服务器端发送数据上面Socket类的常用方法,其中getInputStream()
和getOutStream()
方法分别用于获取输入流和输出流。当客户端和服务端建立连接后,数据是以I/O流的形式进行交互,从而实现通信的。接下来通过一张图来描述服务器端和客户端的数据传输。
public class TCPServer { public static void main(String[] args) { ServerSocket server = null; try{ // 创建服务器端,指定端口号 server = new ServerSocket(7788); while (true){ //创建客户端对象,用于服务端接收数据 Socket clientSocket = server.accept(); // 若没有客户端进行连接将处于阻塞状态 System.out.println("与客户端成功连接,开始进行数据交互!"); // 获取客户端的输出流对象 OutputStream os = clientSocket.getOutputStream(); // 当客户端连接到服务端的时候,向客户端输出数据 os.write("服务器向客户端做出响应".getBytes()); // 做出响应 --- 第二次握手 // 模拟客户端交互耗时 Thread.sleep(500); // 关闭流和连接 os.close(); clientSocket.close(); } } catch (Exception e){ e.printStackTrace(); } }
public class TCPClient { public static void main(String[] args) { Socket client = null; try { // 创建一个客户端连接到指定的服务器端 发送请求 --- 第一次握手 client = new Socket(InetAddress.getLocalHost(),7788); // 获取服务器端返回的输入流数据并打印 InputStream is = client.getInputStream(); byte[] buf = new byte[1024]; int len = 0 ; while ((len = is.read(buf))!=-1){ System.out.println(new String(buf,0,len)); is.read(buf); } // 关闭流和连接 is.close(); client.close(); } catch (Exception e){ e.printStackTrace(); } }}
转载地址:http://bghq.baihongyu.com/