第七小节之网络编程

OSI:开放式系统互联,国际标准化组织,该模型定义了不同计算机互联的标准,是设计和描述计算机网络通信的基本框架。OSI模型把网络通信的工作分为七层,分别是物理层、数据链路层、网络层、传输层会话层、表示层和应用层

ISO:国际标准化组织,对感光度做了量化规定(衡量了底片对于光的灵敏程度)

端口port:0~65535:可以认为是计算机与外界通讯交流的出口。其中硬件领域的端口又称接口,软件领域的端口一般指网络中面向连接服务和无连接服务的通信协议端口,是一种抽象的软件结构,包括一些数据结构和I/O缓冲区

域名:是由一串用点分隔的名字组成的Internet上某一台计算机或计算机组的名称,用在数据传输时标识计算机的电子方位(有时也指地理位置),网络域名系统(DNS)是因特网的一项核心服务,它作为可以将域名和IP地址相互映射而不用记住就能够被机器直接读取的IP地址数串

通信协议由三部分组成:一是语义,用于决定双方对话的类型,二是语法部分,用于决定双方对话的格式,三是交换规则,用于决定通信双方的应答关系。

IP地址:标识号、可以唯一的标识一台计算机

通过IP地址可以连接到指定计算机,但如果想访问目标计算机中的某个应用程序,还需要指定端口号。在计算机中,不同的应用程序是通过端口号区分的。端口号是用两个字节(16位的二进制数)表示的,它的取值范围是0~65535,其中,0~1023之间的端口号用于一些知名的网络服务和应用,用户的普通应用程序需要使用1024以上的端口号,从而避免端口号被另外一个应用或服务所占用。

位于网络中的一台计算机可以通过IP地址去访问另一台计算机,并且通过端口号访问目标计算机中的某个应用程序。

小节介绍了IP地址的作用,JDK中提供了一个InetAddress类,该类用于封装一个IP地址,并提供了一-系列与IP地址相关的方法:  

InetAddress getByName( String host)参数host表示指定的主机,该方法用于在给定主机名的情况下确定主机的IP地址

InetAddress getLocalHost()创建一个表示本地主机的InetAddress对象

String getHostName()得到IP地址的主机名,如果是本机则是计算机名,不是本机则是主机名,如果没有域名则是IP地址

boolean isReachable( int timeout)判断指定的时间内地址是否可以到达
String getHostAddress( )得到字符串格式的原始IP地址

UDP与TCP协议

  UDP:用户数据协议:UDP是无接通信协议即在数据传输时,数据的发送嘴和接收满不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发达数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输

  TCP:传输控制协议:TCP协议是面向连因的通信协议,即在传输数据前先在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差帮的数据传输。在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过3次握手”。第一次握手,客户端向服务器端发出连接请求,等待服务器确认:第二次握手,服务器端向客户端回送一个响应.涌知客户端收到了连接请求;第三次握手:客户端再次向服务器端发送确认信息,确认连接

注意:由于TCP协议的面向连接特性,它可以保证传输数据的安全性

  前面介绍了UDP是一种面向无连接的协议,因此,在通信时发送端和接收端不用立连接。UDP通信的过程就像是货运公司在网个码大同反达页物一样。在码头发送和接收货物时都需要使用集装箱来装载货物,UDP通信也是一样,发送和接收的数据也需要使用“集装箱”进行打包。为此JDK中提供了一个DatagramPacket类,该类的实例对象就相当于一个集装箱,用于封装UDP通信中发送或者接收的数据。
想要创建一个DatagramPacket对象,首先需要了解一下 它的构造方法。在创建发送端和接收端的DatagramPacket对象时,使用的构造方法有所不同,接收端的构造方法只需要接收一.个字节数组来存放接收到的数据,而发送端的构造方法不但要接收存放了发送数据的字节数组,还需要指定发送端IP地址和端口号。接下来根据API文档的内容,对DatagramPacket的构造方法进行详细地讲解。
●DatagramPacket( byte[] buf ,int length)
使用该构造方法在创建DatagramPacket对象时,指定了封装数据的字节数组和数据的大小,没有指定IP地址和端口号。很明显,这样的对象只能用于接收端,不能用于发送端。因为发送端一定要明确指出数据的目的地(IP地址和端口号),而接收端不需要明确知道数据的来源,只需要接收到数据即可。●DatagramPacket( byte[] buf,int length, InetAddress addr ,int port)
使用该构造方法在创建DatagramPacket对象时,不仅指定了封装数据的字节数组和数据的大小,还指定了数据包的目标IP地址(addr)和端口号(port)。该对象通常用发送端,因为在发送送数据时必须指定接收端的IP地址和端口号,就好像发送货物的集装箱上面必需标明接收人的地址一样。

●DatagramPacket(byte[]buf,int ofsetintn length)

该构造方法与第一个构造方法类似,同样用于接收端,只不过在第一个构造方法的基础上,增加了一个offset参数,该参数用于指定接收到的数据在放人buf 缓冲数组时是从offset处开始的。

●DatagramPacket( byte[] buf,int offset , int length, InetAddress addr , int port)

该构造方法与第二个构造方法类似,同样用于发送端,只不过在第二个构造方法的基础上,增加了一个offset参数,该参数用于指定一个数组中发送 数据的偏移量为offset,即从offset位置开始发送数据。

InetAddress getAddress( )该方法用于返回发送端或者接收端的IP地址,如果是发送端的DaramPacket对象,就返回接收端的IP地址,反之,试返回发送的P地址

int getPort()该方法用于返回发送端或者接收端的端口号,如果是发送端的DamPake对象,就返回接收端的端口号,反之.就返回发送端的端口号

byte[] getData()该方法用于返回将要接收或者将要发送的数据,如果是发送端的DatagramPacket对象,就返回将要发送的数据.反之,就返回接收到的数据

int getLength()该方法用于返回接收或者将要发送数据的长度如果是发送端的DatagramPacket对象,就返回将要发送的数据长度反之,就返回接收到数据的长度

  小节讲到Dasrameke数据包的作用就如同是“集装箱"”,可以将发送端或者接收端的数据封装起来.然而运输货物只有"集装箱”“是不够的,还需要有妈头,在程序中需要实现通信只有DatagrmPacke数据包也同样不行,为此JDK提供了一一个DatagramSocket类。DatagramSocket 类的作用就类似于码头,使用这个类的实例对象就可以发送和接收DatagramPacket数据包,

  DatagramSocket()该构造方法用于创建发送满的DaramSocker对象在创建Darasockee对象时,并没有指定端口号,此时,系统会分配一个没有被其他网络程序所使用的端口号。
  DatagramSocket(int port)该构造方法既可用创建接收端的DatagramSocket对象,又可以创建发送端的DatagramSocket对象,在创建接收端的DatagramSocket对象时,必须要指定一个端口号.这样就可以监听指定的端口。

  DatagramSocket(int port, Inet Address addr)使用该构造方法在创建DatagramSocket时,不仅指定了端口号,还指定了相关的IP地址.这种情况适用于计算机上有多块网卡的情况,可以明确规定数据通过哪块网卡向外发送和接收哪块网卡的数据。由于计算机中针对不同的网卡会分配不同的IP,因此在创建DatagramSocket对象时需要通过指定IP地址来确定使用哪块网卡进行通信。

  void receive( DatagramPacket p)该方法用干将接收到的数据填充到DatagramPacket数据包中,在接收到数据之前会一直处 于阻塞状态,只有当接收到数据包时,该方法才会返回
  void send(DatagramPacket p)该方法用于发送DatagramPacket数据包,发送的数据包中包含将要发送的数据.数据的长度、远程主机的IP地址和端口号
  void close()关闭当前的Socket,通知驱动程序释放为这个Socket保留的资源
注意:要实现UDP通信需要创建一个发送端程序和一个接收端程序,很明显,在通信时只有接收端程序先运行,才能避免因发送端发送的数据无法接收,而造成数据丢失。
 1 package udp;
 2 
 3 import java.io.IOException;
 4 import java.net.DatagramPacket;
 5 import java.net.DatagramSocket;
 6 import java.net.InetAddress;
 7 import java.net.SocketException;
 8 import java.net.SocketTimeoutException;
 9 
10 public class UDPClient {
11 
12     public static void main(String[] args) {
13         try {
14             DatagramSocket socket = new DatagramSocket();
15             socket.setSoTimeout(5000);
16             // 构造发送的信息Packet
17             String msg = "你吃早饭了吗?";
18             // Data
19             byte[] data = msg.getBytes("utf-8");
20             // Address
21             InetAddress address = InetAddress.getLocalHost();
22             // Port
23             int port = 8000;
24             // Packet
25             DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
26             // 发送
27             socket.send(packet);
28             System.out.println("发送完成!");
29             
30             // 接受确认信息
31             data = new byte[1024];
32             packet = new DatagramPacket(data, data.length);
33             
34             socket.receive(packet);
35             
36             msg = new String(data, 0, packet.getLength(), "utf-8");
37             if ("ok".equals(msg)) {
38                 System.out.println("发送信息成功!");
39             }
40             
41             socket.close();
42         } catch (SocketException e) {
43             e.printStackTrace();
44         } catch (SocketTimeoutException e) {
45             e.printStackTrace();
46             System.out.println("发送超时,发送失败!");
47         } catch (IOException e) {
48             e.printStackTrace();
49         }
50     }
51 
52 }
53 package udp;
54 
55 import java.io.IOException;
56 import java.net.DatagramPacket;
57 import java.net.DatagramSocket;
58 import java.net.InetAddress;
59 import java.net.SocketException;
60 
61 public class UDPServer {
62 
63     public static void main(String[] args) {
64         try {
65             DatagramSocket socket = new DatagramSocket(8000);
66             System.out.println("服务器开始监听...");
67 
68             byte[] buffer = new byte[1024];
69             
70             while (true) {
71                 DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
72                 socket.receive(packet);
73                 // IP
74                 InetAddress ip = packet.getAddress();
75                 // Port
76                 int port = packet.getPort();
77                 // Data Len
78                 int len = packet.getLength();
79                 // MSG
80                 String msg = new String(buffer, 0, len, "utf-8");
81 
82                 System.out.printf("收到来自[%s:%d]信息:%s
", ip, port, msg);
83                 
84                 // 回复确认                
85                 packet = new DatagramPacket("ok".getBytes(), 2, ip, port);
86                 socket.send(packet);
87                 System.out.println("发送确认完成!");
88             }
89 
90         } catch (SocketException e) {
91             e.printStackTrace();
92         } catch (IOException e) {
93             e.printStackTrace();
94         }
95     }
96 
97 }
TCP通信:TCP通信同UDP通信一样,都能实现两台计算机之间的通信,通信的两端都需要创建Socket对象。区别在于,UDP中只有发送端和接收端,不区分客户端与服务器端.计算机之间可以任意地发送数据。而TCP通信是严格区分客户端与服务器端的,在通信时,必须先由客户端去连接服务哭端才能实现通信,服务器端不可以主动连接客户端,并且服务器端程序需要事先启动等待客户端的连接。
  在JDK中提供了两个类用于实现TCP程序,一个是ServerSocket类,用于表示服务器端。一个是Socket类,用于表示客户端。通信时,首先创建代表服务器端的ServerSocket 对象,该对象相当于开启一个服务,并等待客户端的连接,然后创建代表客户端的Socket对象向服务器端发出连接请求,服务器端响应请求,两者建立连接开始通信。

ServerSocket
  ●ServerSocket()使用该构造 方法在创建ServerSocket对象时并没有绑定端口号,这样的对象创建的服务器端没有监听任何端口,不能直接使用,还需要继续调用bind( SocketAddress endpoint)方法将其绑定到指定的端口号上,才可以正常使用。

.    ●ServerSocket(int port)使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上(参数port就是端口号)。端口号可以指定为0此时系统就会分配-个还没有被其他网络程序所使用的端口号。由于客户端需要根据指定的端口号来访问服务器端程序,因此端口号随机分配的情况并不常用,通常都会让服务器端程序监听一个指定的端口号。

  。ServerSocket(int port, int backlog)该构造方法就是在第二个构造方法的基础上,增加了一个Manklo参数议数用于指定在服务器忙时,可以之保持连接请求的各的客已爱国如见设有货定这个参数,默认为50

  。ServerSocket(int port, int backlog, InetAddress bindAddr)该构造方法就是在第三个构造方法的基础上,还指定了相关的IP地址,这种情况适用于计算机上有多块网卡和多个JP 的情况:我们可以明确规定ServerSacket在哪块网卡或IP地址上等待客户的连接请求。显然,对于一 般只有一块网卡的情况,就不用专门的指定了。

Socket accept()该方法用于等待客户端的连接在客户端连接之前一百处王明室状态,如果有客户端连接就会返回一个与之对应的Soker对象

InetAddress getInetAddress()该方法用于返回一个InetAddress对象,该对象中封装了ServerSocket绑定的IP地址

boolean isClosed()该方法用于判断ServerSocket对象是否为关闭状态.如果是关闭状态则返回true,反之则返回false

void bind(SocketAddress endpoint)该方法用于将ServerSocket 对象绑定到指定的IP地址和端口号,其中参数endpoint封装了IP地址和端口号

注意:ServerSocket对象负责监听某台计算机的某个端口号,在创建ServerSocket对象后,需要继续调用该对象的accept(O方法,接收来自客户端的请求。当执行了accept()方法之后,服务器端程序会发生阻塞,直到客户端发出连接请求,accept()方法才会返回一个Scoket对象用于和客户端实现通信,程序才能继续向下执行。

Socket:

  。Socket()

使用该构造方法在创建Socket对象时,并没有指定IP地址和端口号,也就意味着只创建了客户端对象,并没有去连接任何服务器。通过该构造方法创建对象后还需调用connect(SocketAddress endpoint)方法,才能完成与指定服务器端的连

接,其中参数endpoint用于封装IP 地址和端口号

      。Socket(String host, int port)使用该构造方法在创建Socket对象时,会根据参数去连接在指定地址和端口上  辅运行的服务器程序.其中参数host接收的是一个字符串类型的IP地址

      。Socket( InetAddress address, int port)该方法在使用上与第二个构造方法类似,参数address用于接收一个InetAddress类型的对象,该对象用于封装一个IP地址。    

int getPort( )该方法返回一个int类型对象,该对象是Sadket 对象与服务器端连接的瑞口号

InetAddress getLocalAddressO)该方法用于获取Socket对象绑定的本地IP地址,并将IP地址封装成InetAddress类型的对象返回

void close( )该方法用于关闭Sooket连接结束本次通信。 在关闭Socket之前,应将与Socket相关的所有的输人输出流全部关闭,这是因为一个良好的程序应该在执行完毕时释放所有的资源

InputStream getInputStream()该方法返回一个InputStream类型的输人流对象,如果该对象是由服务器端的Socket返回,就用于读取客户端发送的数据,反之,用于读取服务器端发送的数据

OutputStream getOutputStream()该方法返回一个OutputStream类型的输出流对象,如果该对象OutputStream getOutputStream是 由服务器端的Socket返回,就用于向客户端发送数据,反之,用于向服务器端发送数据

注意:当客户端和服务端建立连接后,数据是以I0流的形式进行交互的,从而实现通信。接下来通过一张图来描述服务器端和客户端的数据传输

 1 package tcp;
 2 
 3 import java.io.IOException;
 4 import java.io.OutputStream;
 5 import java.net.InetAddress;
 6 import java.net.Socket;
 7 import java.net.UnknownHostException;
 8 
 9 public class TcpClient {
10 
11     public static void main(String[] args) {
12         try {
13             InetAddress address = InetAddress.getByName("127.0.0.1");
14             int port = 8000;
15             
16             // 发送连接请求到服务器
17             Socket socketB = new Socket(address, port);
18             System.out.println("连接服务器成功!");
19             
20             // 发送信息到服务器
21             String msg = "你好!";
22             byte[] buffer = msg.getBytes("utf-8");
23             OutputStream os = socketB.getOutputStream();
24             
25             // 发送
26             os.write(buffer);
27             os.flush();  // 强制刷新缓冲区
28             // 关闭对象
29             os.close();
30             socketB.close();
31         } catch (UnknownHostException e) {
32             e.printStackTrace();
33         } catch (IOException e) {
34             e.printStackTrace();
35         }
36     }
37 
38 }
39 package tcp;
40 
41 import java.io.IOException;
42 import java.io.InputStream;
43 import java.net.ServerSocket;
44 import java.net.Socket;
45 
46 public class TcpServer {
47 
48     public static void main(String[] args) {
49         try {
50             ServerSocket ss = new ServerSocket(8000);
51             System.out.println("前台接待已就位...");
52             // 接受客户端的请求,并建立连接
53             while (true) {
54                 Socket socketA = ss.accept();
55                 System.out.println("已经接收到客户端请求");
56 
57                 // 接受
58                 InputStream is = socketA.getInputStream();
59                 byte[] buffer = new byte[1024];
60                 int len = is.read(buffer);
61                 if (len > 0) {
62                     String content = new String(buffer, 0, len, "utf-8");
63                     System.out.println("服务器收到:" + content);
64                 }
65                 try {
66                     Thread.sleep(2000);
67                 } catch (InterruptedException e) {
68                     e.printStackTrace();
69                 }
70                 System.out.println("交流结束");
71                 // 关闭对象
72                 is.close();
73                 socketA.close();
74             }
75         } catch (IOException e) {
76             e.printStackTrace();
77         }
78     }
79 
80 }

多线程的TCP网络程序:

  服务器端为每个客户端创建一个对应的Socket,并且开启一个新的线程使两个Socket建立专线进行通信。

原文地址:https://www.cnblogs.com/0405mxh/p/10320908.html