TCP/IP协议
TCP/IP协议栈是一系列网络协议的总和,是构成网络通信的核心骨架,它定义了电子设备如何连入因特网,以及数据如何在它们之间进行传输。
TCP/IP协议分层模型
TCP/IP协议的分层有2个模型,分别是TCP/IP参考模型与OSI参考模型。
在TCP/IP参考模型中,是分为数据链路层、互联网IP层、传输层、应用层共4层
在OSI参考模型中分为物理层、数据链路层、网络层、传输层、会话层、表示层、应用层共7层。
TCP协议是目前使用最为广泛的协议,OSI是一个理想模型(硬件跟不上)
OSI协议模型
OSI就是一个开放的通信系统互联参考模型,也是一个定义的很好的协议规范。OSI模型有7层结构,每层都可以有几个子层。OSI的7层从下到上分别是7-应用层、6-表示层、5-会话层、4-传输层、3-网络层、2-数据链路层、1-物理层
物理层:是参考模型的最低层。该层是网络通信的数据传输介质,由连接不同结点的电缆与设备共同构成。主要跟功能是:利用传输介质为数据链路层提供物理连接,负责处理数据传输并监控数据出错率,以便数据流的透明传输。典型设备时集线器HUB
数据链路层:四参考模型的第二层。主要功能是:在物理层提供的服务基础上,在通信的实体间建立数据链路连接,传输以“帧”为单位的数据包,并采用差错控制与流量控制方法,使有差错的物理线路变成无差错的数据链路。典型设备是交换机SWITCH
网络层:是参考模型的第三层。主要功能是:为数据在节点之间传输创建逻辑链路,通过路由选择算法为分组通过通信子网选择最适当的路径,以及实现拥塞控制、网络互连等功能。
传输层:是参考模型的第四层。主要功能是:向用户提供可靠地端到端服务,处理数据包错误、数据包次序,以及其他一些关键传输问题。传输层向高层屏蔽了下层数据通信的细节。因此,它是计算机通信体系结构中关键的一层。
会话层:是参考模型的第五层。主要功能是:负责维扩两个结点之间的传输连接,以便确保点到点传输不中断,以及管理数据交换等功能。
表示层:是参考模型的第六层。主要功能是:用于处理在两个通信系统中交换信息的表示方法,主要包括数据格式变换、数据加密与解密、数据压缩与恢复等功能。
应用层:是参考模型的最高层。主要功能是:为应用软件提供了很多服务,比如文件服务器、数据库服务、电子邮件与其他网络软件服务。
TCP协议
三次握手
TCP是面向连接的协议,因此每个TCP连接都有3个阶段:连接建立、数据传送和连接释放。连接建立经历三个步骤,通常称为三次握手。可以参考网络状态切换图
第一次握手(客户端发送请求)
客户机发送连接请求报文段到服务器,并进入SYN_SENT状态,等待服务器确认。发送连接请求报文段内容:SYN=1,seq=x;SYN=1意思是一个TCP的SYN标志位置为1的包,指明客户端打算连接的服务器的端口;seq=x表示客户端初始序号x,保存在包头的序列号Sequence Number字段里。
第二次握手(服务端回传确认)
服务器收到客户端连接请求报文,如果同意建立连接,向客户机发回确认报文段ACK应答,并为该TCP连接分配TCP缓存和变量。服务器发回确认报文段内容:SYN=1,ACK=1,seq=y,ack=x+1;SYN标志位和ACK标志位均为1,同时将确认序号(Acknowledgement Number)设置为客户的ISN加1,即x+1;seq=y为服务端初始序号y。
第三次握手(客户端回传确认)
客户机收到服务器的确认报文段后,向服务器给出确认报文段ACK,并且也要给该连接分配缓存和变量。此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。客户端发回确认报文段内容:ACK=1,seq=x+1,ack=y+1;ACK=1为确认报文段;seq=x+1为客户端序号加1;ack=y+1,为服务器发来的ACK的初始序号字段+1。
注意:握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。
计算机网络是通过传输介质、通信设施和网络通信协议,把分散在不同地点的计算机设备互连起来的,实现资源共享和数据传输的系统。网络编程就是编写程序使互联网的两个(或多个)设备(如计算机)之间进行数据传输。Java语言对网络编程提供了良好的支持。通过其提供的接口我们可以很方便地进行网络编程。
IP地址
IP协议有2个版本,分别是IPv4和IPv6,目前IPv4的地址已经耗尽
IPv4采用的是4位点分十进制的计法,例如192.168.4.36,每个位的取值范围为0-255。
由于4位点分十进制的计法很难记忆,所以因为域名 www.baidu.com对应的IP地址为220.181.38.149
Internet依靠DNS实现了机器名和IP地址之间的对应关系,DNS负责完成域名的解析
InetAddress是Java对IP地址的封装。其下有两个子类Inet4Address和Inet6Address。这个类的实例经常和UDP DatagramSockets和Socket,ServerSocket类一起使用
InetAddress 没有公开的构造方法,因此你必须通过一系列静态方法中的某一个来获取它的实例。
不仅可以通过主机名获取ia对象,也可以通过IP地址构建ia对象
还可以通过InetAddress中的方法获取本地机的相关信息
特殊方法isReachable用于测试是否可以到达该地址,防火墙和服务器配置可能阻塞请求,使得在访问时处于不可达状态
URL访问网上资源
URL对象代表统一资源定位器(通俗就是网址),是指向互联网资源的指针,资源可以是简单的文件或目录,也可以是对复杂对象的引用,例如对数据库或搜索引擎的查询。用协议名、主机、端口和资源组成,即满足格式:protocol://host:port/resourceName,
例如http://www.yan.com:80/index.php
http协议的标准端口为80
https=http+SSL 加密数据传输的http,标准端口为443
最重要的方法:
url.openConnection():URLConnection 可以获取输入、输出流
url.openStream():InputStream 直接获取输入流
下载并保存指定的页面
获取当前网页的所有相关的链接地址
获取URL对象封装的地址信息的不同部分
URL与URLConnection比较
URL和URLConnection的区别在于前者代表一个资源的位置,后者代表一种连接
Java语言提供了两种方法读取数据,一种是通过URL对象直接得到相关的网络信息,另一种是先得到一个URLConnection实例,再得到InputStream或OutputStream对象,然后读取数据
前者是一种简单、直接的方法,但缺乏灵活性,并且只能读取只读信息,后者提供了更加灵活有效的方法来读取网络资源
TCP
TCP传输控制协议,属于传输层协议,提供一个可靠的点到点的虚连接。
在实际应用中TCP网络程序提供可靠的数据通信,而UDP网络程序则不保证数据的可靠性,但是协议简单、传输速度快(比如用在音视频数据传输,它们不需要很高的可靠性,偶尔丢帧是可以忍受的)
TCP是Tranfer Control Protocol的 简称,是一种面向连接的端对端的保证可靠传输的协议。
通过TCP协议传输,得到的是一个顺序的无差错的数据流。
Socket是什么呢?
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
发送方和接收方的成对的两个socket之间必须建立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送 或接收操作。
ServerSocket类
Java.net包中的ServerSocket类用于表示服务器套接字,其主要功能是监听客户端的请求,然后将客户端的请求连接存入队列中,默认请求队列大小是50。
构造方法主要有以下几种形式:
ServerSocket(int port):创建绑定到特定端口的服务器套接字。Port的取值范围为0-65535之间,0表示使用任意未占用端口,建议使用的端口号大于1024。如果端口已经被占用则会BindException
ServerSocket(int port,int backlog,InetAdress bindAddress):使用指定的端口、监听backlog和要绑定到本地IP地址创建服务器,适用于计算机有多个网卡、多个IP的情景
最可怕的中毒事件是木马程序。例如netbus,一般情况下木马会打开一些端口,供远程客户端连接。
使用ServerSocket检测当前机已经打开的端口号
多线程的例子
public class Test1 {
static Set<Integer> set = Collections.synchronizedSet(new HashSet<>()); //7个线程操作同一个Set,所以需要考虑线程安全问题
static CountDownLatch cdl=new CountDownLatch(7); //阻塞主线程,等待7个子线程执行结果
public static void main(String[] args) throws Exception {
for (int i = 0; i < 7; i++) {
int begin = i * 10000 + 1;
int end = (i + 1) * 10000;
if (end > 65535)
end = 65535;
new Thread(new MyRunnable(begin, end)).start();
}
cdl.await(); //注册当前的主线程,等待cdl为0
set.forEach(System.out::println);
}
static class MyRunnable implements Runnable {
private int begin, end;
public MyRunnable(int begin, int end) {
this.begin = begin;
this.end = end;
}
@Override
public void run() {
for (int i = begin; i <= end; i++) {
ServerSocket ss = null;
try {
ss = new ServerSocket(i);
} catch (Exception e) {
set.add(i);
} finally {
try {
if (ss != null) ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
cdl.countDown(); //倒计算器-1
}
}
}
客户端Socket的构造器
Socket(String host, int prot); 在客户端构建Socket对象,如果构建成功则获取对象,否则ConnectException。参数1为链接主机的名称,也可以使用InetAddress表示IP地址;参数2为链接服务器的监听端口号,要求服务器已经打开的链接端口
需求:客户端发起一个请求【hello server】,服务器响应一个信息内容为【server:服务器系统当前时】
1、在TCP编程中Server和client地位不对等,首先需要在服务器端开启监听端口,然后阻塞主线程,等待客户端的连接请求
服务器端:
客户端发起连接请求
2、客户端和服务器分别通过各自的Socket对象获取对应的输入输出流(字节流)。服务器的输出流对应客户端的输入流,服务器的输入流对应客户端的输出流,所以在具体的操作中只是编程操作流,而无需关心数据的传输细节
针对字符数据直接使用字节流比较麻烦,所以可以通过过滤流操作字符
3、具体实现通讯细节
客户端
服务器
4、关闭相关的流和套接字对象,规则是正向打开,逆向关闭
具体代码:
服务器
客户端编码
启动一个服务器对多个客户端请求进行响应
具体实现:一个客户端使用一个线程进行处理,处理完毕自动关闭连接,服务器主线程一直处于监听等待状态
public class Server {
public static void main(String[] args) throws Exception {
ServerSocket ss = null;
try {
ss = new ServerSocket(9000);
while (true) {
Socket s = ss.accept();
if (s != null)
new ServerThread(s).start();
}
} finally {
if (ss != null)
ss.close();
}
}
}
class ServerThread extends Thread {
private Socket socket;
public ServerThread(Socket s) {
this.socket = s;
}
@Override
public void run() {
try (
// 通讯细节提前进行约定。如果客户端hello,则服务器响应当前时;如果客户端bye则服务器针对客户端的线程执行结束
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintStream ps = new PrintStream(socket.getOutputStream());) {
while (true) {
String ss = br.readLine();
if (ss != null) {
System.out.println("客户端" + socket.getInetAddress().getHostAddress() + "说:" + ss);
if ("bye".equals(ss))
break;
if ("hello".equals(ss))
ps.println(new Date().toString());
else {
ps.println("然后呢?");
}
ps.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
客户端:
用户键盘录入数据,用户输入bye退出
传送其它类型数据
ObjectInputStream ois=new ObjectInputStream(socket.getInputStream());
Object obj=ois.readObject();
ObjectOutputStream oos=new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(obj);