一、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对象代表统一资源定位器(通俗就是网址),是指向互联网资源的指针,资源可以是简单的文件或目录,也可以是对复杂对象的引用,例如对数据库或搜索引擎的查询。用协议名、主机、端口和资源组成,即满足格式
最重要的方法:
url.openConnection():URLConnection 可以获取输入、输出流
url.openStream():InputStream 直接获取输入流
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的情景
使用ServerSocket检测当前机已经打开的端口号

客户端Socket的构造器
Socket(String host, int prot); 在客户端构建Socket对象,如果构建成功则获取对象,否则ConnectException。参数1为链接主机的名称,也可以使用InetAddress表示IP地址;参数2为链接服务器的监听端口号,要求服务器已经打开的链接端口
需求:客户端发起一个请求【hello server】,服务器响应一个信息内容为【server:服务器系统当前时】
1、在TCP编程中Server和client地位不对等,首先需要在服务器端开启监听端口,然后阻塞主线程,等待客户端的连接请求
2、客户端和服务器分别通过各自的Socket对象获取对应的输入输出流(字节流)。服务器的输出流对应客户端的输入流,服务器的输入流对应客户端的输出流,所以在具体的操作中只是编程操作流,而无需关心数据的传输细节
3、关闭相关的流和套接字对象,规则是正向打开,逆向关闭
实现一个QQ聊天室
定义一个类用于封装用户的聊天信息,序列化接口
ip地址 时间 内容
定义一个集合,其中最多存放10条数据
public class User implements Serializable {
private String str;
private Date da;
private Object obj;
public User(String str, Date da, Object obj) {
this.str = str;
this.da = da;
this.obj = obj;
}
public User() {
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public Date getDa() {
return da;
}
public void setDa(Date da) {
this.da = da;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
@Override
public String toString() {
return "用户IP地址:" + str + ", 发送内容:" + obj + ", 发送时间:" + da ;
}
}
public class Server extends Obj {
//一个集合只能存10条消息(包含:ip地址 时间 内容),有新用户进来,将该集合输出
static volatile List<User> li = new Vector<>(10);
//一个集合存放所有用户,只要有新消息通知所有用户
static volatile Set<Socket> se = new HashSet<>();
public static void main(String[] args) throws Exception {
ServerSocket so = null;
try {
so = new ServerSocket(8000);
//不断的接收用户请求,并做出响应
while (true) {
Socket socket = so.accept();
if (so != null) {
method(socket);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
end(so);
}
}
public static void method(Socket socket) throws Exception {
new Thread(() -> {
try (
BufferedReader bu = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter pr = new PrintWriter(socket.getOutputStream());
) {
System.out.println(Thread.currentThread().getName());
//新进入聊天室,给当前用户发送历史记录
start(pr);
//判断该用户是否为新用户
exist(socket);
//开始聊天
User user = null;
while (true) {
String str = bu.readLine();
if ("bye".equals(str)) {
//记录消息记录
addList(user, str, socket);
send();
break;
}
if ("hello".equals(str)) {
bb=true;
addList(user, str, socket);
send();
System.out.println(str);
pr.println("你好");
} else {
bb=true;
addList(user, str, socket);
send();
System.out.println(str);
pr.println("你想说什么");
}
pr.flush();
bb=false;
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
public static void start(PrintWriter pr) {
bb=true;
pr.println("欢迎进入XXX聊天室");
pr.flush();
System.out.println(li.size());
if (li.size() > 0) {
pr.println("历史消息为:");
pr.flush();
for (User aa : li) {
pr.println(aa.toString());
pr.flush();
}
} else {
pr.println("当前没有历史消息");
pr.flush();
}
bb=false;
}
public static void exist(Socket socket) {
if (!se.contains(socket)) {
se.add(socket);
}
}
//向记录消息的集合中添加数据
public static void addList(User user, String str, Socket socket) {
user = new User(socket.getInetAddress().getHostAddress(), new Date(), str);
if (li.size() <= 10) {
li.add(user);
} else {
li.remove(0);
li.add(user);
}
}
//向所有用户发送消息
public static void send() throws Exception {
for (Socket ss : se) {
PrintWriter pr = new PrintWriter(ss.getOutputStream());
pr.println(li.get(li.size()-1));
pr.flush();
}
}
public static void end(ServerSocket so) {
try {
if (so != null) {
so.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Socket01 extends Obj {
public static void main(String[] args) throws Exception {
Socket so = new Socket("localhost", 8000);
//键盘录入
BufferedReader bu1 = new BufferedReader(new InputStreamReader(System.in));
//客户端的输入
BufferedReader bu = new BufferedReader(new InputStreamReader(so.getInputStream()));
//向客户端的输出
PrintWriter pr = new PrintWriter(new OutputStreamWriter(so.getOutputStream()));
jieshou(bu);
while (true) {
Thread.sleep(100);
String str = bu1.readLine();
pr.println(str);
pr.flush();
if ("bye".equals(str)) {
break;
}
}
bu.close();
bu1.close();
pr.close();
}
public static void jieshou(BufferedReader bu) {
Thread th = new Thread(() -> {
while (true) {
try {
Thread.sleep(100);
String str1 = bu.readLine();
if (str1 != null) {
System.out.println(str1);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
th.setDaemon(true);
th.start();
}
}
562

被折叠的 条评论
为什么被折叠?



