关于Java网络编程
计算机网络将地理位置不同的计算机通过通信线路连接起来,实现资源的共享和信息的传递,网络中的计算机通常称之为主机,而网络编程就是通过程序来实现两台以上主机之间的通信。
实际的通信网络内容较多,但是Java语言提供了许多网络类,屏蔽了底层的复杂细节,是程序员可以很容易地编写出网络程序。
计算机网络知识基础
在进行Java网络编程之前,需要对关于计算机网络的总体知识有一个大致的理解。
分层结构模型
众所周知,计算机网络传输实际上是一个很复杂的模型,为了更方便程序开发,我们一般会简历网络传输模型,将各个不同的层次分隔开,由不同的人来设计不同的层次,这样设计各个层次的人就不用管其他层面的具体实现,每一层都呼叫它的下一层所提供的网络来完成自己的需求,只需要调用别人的服务就可以了,大大提高了工作效率。除此之外,因为各层之间是独立的,结构上可分割开,所以灵活性好,易于实现和维护,能促进标准化工作:差错控制,流量控制,分段和重装,复用和分用,连接建立和释放。
常见的模型由三种:
1.OSI七层体系
2.五层体系
5层只是OSI和TCP/IP的综合,是业界产生出来的非官方协议模型,但是很多具体的应用。实际应用还是TCP/IP的四层结构。
3.TCP/IP四层体系
该体系将数据链路层和物理层合并为网络接口层。
总结
OSI总结图
TCP/IP协议与其的关系()
这是其中有关于协议的数据报文格式:
Java网络编程基础
计算机在网络通信协议中必须遵守一定的规则,就好比车辆行驶要遵守交通规则一样,在计算机网络协议中,这些连接和通信的规则被称为网络通信协议,它对传输格式,传输速率,传输步骤做了统一的规定,通信双方必须遵守这些协议才能进行数据交换;
网络通信协议有很多种,但是目前主要使用的还是TCP/IP、UDP、ICMP和一些其他协议;
IP地址与端口号
要想使计算机能够通信,必须要为每台计算机指定标识号,通过标识号来指定接收或发送数据的计算机,在TCP/IP协议中,这个标识号就是IP地址,目前普遍使用的是4个字节的IPV4,不过为了解决日渐枯竭的网络地址资源不足,现在16个字节IPV6也应运而生;
IP地址一般由两部分组成,是 网络号+主机号,网络号指定网络地址,主机号指定该网络中的主机地址,二者是主从关系;
通过IP地址可以连接到指定计算机,但是如果想要访问计算机中某个应用程序,还需要指定端口号,在计算机中,不同的应用程序是通过端口号来区分的,端口号是用两个字节(16位的二进制数)来表示的,取值范围为0到65533,但其中0到1023的端口号被系统的网络服务所占有,所以用户的普通应用的端口号不能属于这个范围;
总得来说,一台计算机可以通过IP地址来访问到网络中的另一台计算机,并通过端口号来访问这个计算机中的某个应用程序;
InetAddress类
这是Java中提供的与IP地址相关的InetAddress类,该类用于封装IP地址,并提供了一系列与IP地址相关的方法,该类的方法有以下几个,例如:
InetAddress getByName(String host) 得到IP地址封装为InetAddress类
InetAddress getLocalHost() 创建一个表示本地主机的InetAddress对象
String getHostName() 得到字符串格式的原始IP地址
String getHostHome() 得到确定的IP地址的主机名
boolean isReachable(int timeout) 判断在指定的时间内地址是否可以到达
TCP和UDP
TCP和UDP是两个传输层的高级协议;
UDP是用户数据报协议,TCP是传输控制协议;
UDP是无连接通信协议,在数据传输的时候,数据的发送端和接收端并不建立逻辑连接,简单来说,当一台计算机向另外一台计算机发送数据时,并不好确认接收端是否存在,就直接发送数据,接收端在接受数据的时候,也不会向发送端反馈是否收到数据。
由于UDP消耗资源小,传输效率高,,所以通常用于音视频和普通数据的传输,比如说直播视频,但是由于UDP的无连接性,不能保证数据传输的完整性,所以在传输重要数据的时候不建议使用UDP。
TCP是面向连接的通信协议,即在传输数据的时候先在发送端和接收端建立逻辑连接,然后再传输数据;这提供了两台计算机之间可靠的,无差错的数据传输;
由于TCP的面向连接的特性,它可以保证数据传输的安全性,所以是一个被广泛采取的协议,比如在下载压缩包文件的时候,就非常适合使用TCP连接。
在TCP连接过程中,必须要明确客户端和服务器端,由客户端来向服务器端发出连接请求;
每次连接都需要经历三次握手协议;
三次握手协议:
第一次握手,客户端向服务器端发出连接请求,等待服务器端的确认;
第二次握手,服务器端向客户端回复一个响应,通知客户端收到了连接请求;
第三次握手,客户端向服务器端发送确认信息,确认连接;
而在TCP断开链接的时候,就需要用到四次挥手协议
第一次挥手,若A认为数据发送完成,则它需要向B发送连接释放请求。
第二次挥手,B收到连接释放请求后,会通知相应的应用程序,告诉它A向B这个方向的连接已经释放。
第三次挥手,当B向A发完所有数据后,向A发送连接释放请求
第四次挥手,A收到释放请求后,向B发送确认应答。
TCP协议作为一个可靠的面向流的传输协议,其可靠性和流量控制由滑动窗口协议保证,而拥塞控制则由控制窗口结合一系列的控制算法实现,以及快重传和快恢复,超时重传来确保文件完整性。
两者所在层次:
两者特点比较:
TCP网络编程
ServerSocket
用于建立TCP连接,该类可以实现一个服务端的程序;
1、构造ServerSocket
ServerSocket()throws IOException //ServerSocket有一个不带参数的默认构造方法。通过该方法创建的ServerSocket不与任何端口绑定,接下来还需要通过bind()方法与特定端口绑定,这个默认构造方法的用途是,允许服务器在绑定到特定端口之前,先设置ServerSocket的一些选项。因为一旦服务器与特定端口绑定,有些选项就不能再改变了。
ServerSocket(int port) throws IOException //使服务器与特定端口绑定,该端口由参数port指定
ServerSocket(int port, int backlog) throws IOException //参数backlog指定客户连接请求队列的长度
ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException //参数bindAddr指定服务器要绑定的IP地址
2.使用ServerSocket进行服务端编程
import java.io.*;
import java.net.*;
import java.util.*;
public class qwe {
public static void main(String[] args) {
ServerSocket server = null; //创建服务器Socket对象
Socket socket = null; //创建Socket对象,用于接收另一端socket的连接
DataInputStream in = null;
DataOutputStream out = null; //准备好IO流
int port = 5050;//设置端口号
try{
server = new ServerSocket(port);
}
catch (IOException e){
System.out.println(e);
}//创建ServerSocket对象,绑定5050端口
try{
System.out.println("服务器启动");
socket = server.accept();//准备监听Socket连接并接收
in = new DataInputStream(socket.getInputStream());//返回一个输入流对象
out = new DataOutputStream(socket.getOutputStream());//返回一个输出流对象
//socket之间是通过IO流来交互信息的
String str = in.readUTF();
System.out.println("收到信息为:"+str);
out.writeUTF("你好");
}catch(Exception e){
System.out.println(e);
}
finally {
//准备关闭连接
try{
out.close();
in.close();
socket.close();//Socket对象的关闭连接方法
server.close();
}catch (Exception e){
System.out.println(e);
}
}
}
}
Socket(客户端)
import java.io.*;
import java.net.*;
public class qwe {
public static void main(String[] args){
Socket client = null;
DataInputStream in = null;
DataOutputStream out = null;
String ip = "127.0.0.1";
int port = 5050;
try
{
client = new Socket(ip,port);//客户端socket绑定端口和ip地址
in = new DataInputStream(client.getInputStream());
out = new DataOutputStream(client.getOutputStream());
out.writeUTF("这里是客户端");
String str = ((DataInputStream) in).readUTF();
System.out.println(str);
}catch (Exception e){
System.out.println(e);
}finally {
try{
in.close();
out.close();
client.close();
}catch (Exception e){
System.out.println(e);
}
}
}
}
UDP网络编程
UDP是一种无连接的不可靠传输协议,在使用UDP进行网络连接通信的时候,不需要建立连接,所以速度块,发送的时候需要封装成数据包,就像把信件装入信封。
DatagramPacket
UDP通信过程中并不建立连接,就像是两个货运公司在两个码头之间运送货物一样,在码头发送和接受货物都需要使用集装箱来装载货物。UDP通信也是一样,我们需要使用DatagramPacket类,该类的实例对象就相当与一个集装箱,也可以说是一个信封,我们就利用这个东西对我们需要发送的文件进行打包发送,总之,这个类是用来发送的。
构造方法:
DatagramPacket(byte[] data, int length) //指定了封装信息的字节数组大小,以及数组的长苏
DatagramPacket(byte[] data, int offset, int length) //在上面的基础上,增加了offset,用于指定数据的缓冲位置
DatagramPacket(byte[ ] data, int length, InetAddress address, int port) //增加了指定的目标IP地址address和目的端口号port
DatagramPacket(byte[] data, int offset, int length, InetAddress address, int port) //完整版
常用方法:
InetAddress getAddress() //返回IP地址
int getPort() //返回端口号
byte[] getData() //返回打包的数据
int getLength() //返回数据字节数组的长度
DatagramSocket
如果说DatagramPacket相当于一个集装箱,然而,两个货运公司之间的传输仅仅使用集装箱是不够的,我们还需要一个码头进行接收集装箱,在Java中,我们使用DatagramSocket来当作“码头”,使用该类的实例对象就可以接收或发送DatagramPacket数据包,总之,这个类是用来接收的。
构造方法:
DatagramSocket() //用于创建一个DatagramSocket对象,如果没有给端口号,系统会从其他应用自动分配一个
DatagramSocket(int port) //指定了端口号,这样对象可以监听指定的端口
DatagramSocket(int port,InetAddress addr) //指定了端口号和IP地址,该对象适用与计算机上有多块网卡的情况,因为计算机针对不同的网卡会分配不同的IP地址,所以指定IP地址就可以确定你到底要使用哪块网卡来通信
void receive(DatagramPacket p) //接收包
void send(DatagramPacket p) //发送包
void close //关闭
UDP编程实例
import java.io.*;
import java.net.*;
public class qwe {
public static void main(String[] args) {
DatagramSocket socket = null;//码头,利用其的send和receive方法来进行发送和接收
DatagramPacket packet_send = null;//集装箱,用于发送
DatagramPacket packet_receive = null;//集装箱,用于接收
int port = 5050;
String ipAddress = "127.0.0.1";
try{
socket = new DatagramSocket(port);//建立好连接
byte[] r = bew byte[1024];
packet_receive = new DatagramPacket(r,r.length);
socket.receive(packet_receive);//接受从另一端发送过来的数据
InetAddress address = InetAddress.getByName(ipAddress);//封装好InetAddress
String str = "服务端发送数据";
byte[] r = str.getBytes();//开始打包
packet_send = new DatagramPacket(r,r.length,address,port);//打包好数据
socket.send(packet_send);//将数据包发送到另一端
}
catch (Exception e)
{
System.out.println(e);
}
finally {
socket.close();//关闭连接
}
}
}