Java高并发网络编程(一)
一、OSI网络七层模型
因特网是一个极为复杂的网络,分层有助于我们对网络的理解 。分层也是一种标准,为了使不同厂商的计算机能够互相通信,以便在更大范围内建立计算机网络,有必要建立一个国际范围的网络体系结构标准。


ISO组织制定了OSI网络七层模型
| 应用层 |
| 表示层 |
| 会话层 |
| 传输层 |
| 网络层 |
| 链路层 |
| 物理层 |
而因特网只用到了五层
| 应用层 |
| 传输层 |
| 网络层 |
| 链路层 |
| 物理层 |
低三层:
屏蔽底层网络的复杂性
物理层:使原始的数据比特流能在物理介质上传输。
数据链路层:通过校验、确认和反馈重发等手段,形成稳定的数据链路。(01010101)
网络层:进行路由选择和流量控制。(IP协议)
传输层:提供可靠的端口到端口的数据传输服务(TCP/UDP协议)。
高三层:
会话层:负责建立、管理和终止进程之间的会话和数据交换。
表示层:负责数据格式转换、数据加密与解密、压缩与解压缩等。
应用层:为用户的应用进程提供网络服务。
网络通信协议

二、传输层控制协议TCP
传输层控制协议(TCP)是Internet一个重要的传输层协议。TCP提供面向连接、可靠、有序、字节流传输服务。应用程序在使用TCP之前,必须先建立TCP连接。


1.TCP握手机制
检测网络是否通畅


三、用户数据报协议UDP
用户数据报协议UDP是Internet传输层协议。提供无连接、不可靠、数据尽力传输服务。


TCP和UDP比较

四、Socket
1.Scoket概述
IP地址:InetAddress
- 唯一的标识Internet上的计算机
- 本地回环地址(hostAddress):127.0.0.1 主机名(hostName):localhost
- 不易记忆
端口号标识正在计算机上运行的进程(程序)
- 不同的进程有不同的端口号
- 被规定为一个16位的整数0~65535。其中,0~1023被预先定义的服务通信占用(如MySql占用端口3306,http占用端口80等)。除非我们需要访问这些特殊服务,否则,应该使用1024~65535这些端口中的某一个进行通信,以免发生端口冲突。
端口号与IP地址的组合得出一个网络套接字
我们知道两个进程如果需要进行通讯最基本的一个前提能能够唯一的标示一个进程,在本地进程通讯中我们可以使用PID来唯一标示一个进程,但PID只在本地唯一,网络中的两个进程PID冲突几率很大,这时候我们需要另辟它径了,我们知道IP层的ip地址可以唯一标示主机,而TCP层协议和端口号可以唯一标示主机的一个进程,这样我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程。
能够唯一标示网络中的进程后,它们就可以利用socket进行通信了,什么是socket呢?我们经常把socket翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。

网络应用程序由成对的进程组成,这些进程通过网络相互发送报文。在一对进程之间的通信会话场景中,发起通信的进程被标识为客户,在会话开始时等待联系的进程是服务器。进程通过一个称为套接字的软件接口向网络发送报文和从网络接收报文。套接字是同一台主机内应用层与传输层之间的接口,由于该套接字是建立网络应用程序的可编程接口,因此套接字也称为应用程序和网路之间的应用程序编程接口(API)。应用程序开发者可以控制套接字在应用层端的一切,但是对该套接字的运输层端几乎没有控制权,因此网络编程实际上就是Socket编程。
Internet应用最广的网络应用编程接口,实现与3种底层协议接口:
- 数据报类型套接字SOCK_DGRAM(面向UDP接口)
- 流式套接字SOCK_STREAM(面向TCP接口)
- 原始套接字SOCK_RAW(面向网络层协议接口IP、ICMP等)
主要socket API及其调用过程


socket是"打开—读/写—关闭"模式的实现,以使用TCP协议通讯的socket为例,其交互流程大概如下:
2.Java中的Socket编程
客户端
public class SocketClient {
public void client() {
Socket sc = null;
OutputStream os = null;
InputStream is = null;
try {
sc = new Socket(InetAddress.getByName("127.0.0.1"), 9092);
os = sc.getOutputStream();
os.write("我是客户端,我向你发送消息了".getBytes());
sc.shutdownOutput(); // 显式的告诉服务端发送完毕
is = sc.getInputStream();
byte[] b = new byte[30];
int len;
while ((len = is.read(b)) != -1) {
String str = new String(b, 0, len);
System.out.println(str);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (sc != null) {
try {
sc.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
SocketClient client = new SocketClient();
client.client();
}
}
当Client程序需要从Server端获取信息及其他服务时,应创建一个Socket对象
构造函数

new Socket(InetAddress.getByName("127.0.0.1"), 9092)会打开一个套接字
一旦套接字被打开,java.net.Socket类中的getInputStream方法就会返回一个InputStream对象,该对象可以像其他任何流一样使用,接收另一端传来的数据
getOutPutStream则是输出流,可以传给另一端
套接字离不开IP地址, Java中有IP相关的类
InetAddress类没有提供公共的构造方法,而是提供了两个静态方法来获取InetAddress实例

InetAddress有如下常用方法:

服务器端
public class SocketServer {
public void server() {
ServerSocket ss = null;
Socket s = null;
InputStream is = null;
OutputStream os = null;
try {
ss = new ServerSocket(9092);
s = ss.accept();
is = s.getInputStream();
/**
* 1、 read()方法,这个方法从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。 2、read(byte[] b,int
* off,int len)方法,将输入流中最多 len 个数据字节读入 byte 数组。尝试读取len 个字节,但读取的字节也可能小于该值。以整数形式返回实际读取的字节数。 3、read(byte[]
* b)方法,从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回实际读取的字节数。
*/
byte[] b = new byte[30];
int len;
while ((len = is.read(b)) != -1) {
/**
* new String(bytes, offset, length) bytes为要解译的字符串; offset为要解译的第一个索引,比如从0开始就是从字符串bytes的第一个字符开始;
* length为要解译的字符串bytes的长度
*/
String str = new String(b, 0, len);
System.out.println(str);
}
os = s.getOutputStream();
os.write("我是服务端,收到了你的信息".getBytes());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (s != null) {
try {
s.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (ss != null) {
try {
ss.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
SocketServer server = new SocketServer();
server.server();
}
}
服务器需随时待命,因为不知道客户端什么时候会发来请求,此时,我们需要使用ServerSocket,对应的是java.net.ServerSocket类
构造函数

new ServerSocket(9092)建立一个监听9092接口的服务器
ss.accept()告诉程序不停的等待,直到客户端连接到这个接口。一旦有人通过网络发送了正确的连接请求,并以此连接到了端口上,该方法就返回一个表示连接已经建立的Socket对象。
可以使用这个对象来得到输入流和输出流
Java高并发网络编程(一)的更多相关文章
- Java高并发网络编程(四)Netty
在网络应用开发的过程中,直接使用JDK提供的NIO的API,比较繁琐,而且想要进行性能提升,还需要结合多线程技术. 由于网络编程本身的复杂性,以及JDK API开发的使用难度较高,所以在开源社区中,涌 ...
- Java高并发网络编程(三)NIO
从Java 1.4开始,Java提供了新的非阻塞IO操作API,用意是替代Java IO和Java Networking相关的API. NIO中有三个核心组件: Buffer缓冲区 Channel通道 ...
- Java高并发网络编程(五)Netty应用
推送系统 一.系统设计 二.拆包和粘包 粘包.拆包表现形式 现在假设客户端向服务端连续发送了两个数据包,用packet1和packet2来表示,那么服务端收到的数据可以分为三种,现列举如下: 第一种情 ...
- Java高并发网络编程(二)BIO
一.阻塞 服务器端 public class BIOServer { public static void main(String[] args) throws Exception { ServerS ...
- Linux下高并发网络编程
Linux下高并发网络编程 1.修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时, 最高的并发数量都要受到系统对用户单一进程同时可打 ...
- Linux高并发网络编程开发——10-Linux系统编程-第10天(网络编程基础-socket)
在学习Linux高并发网络编程开发总结了笔记,并分享出来.有问题请及时联系博主:Alliswell_WP,转载请注明出处. 10-Linux系统编程-第10天(网络编程基础-socket) 在学习Li ...
- 从菜鸟到大神:Java高并发核心编程(连载视频)
任何事情是有套路的,学习是如此, Java的学习,更是如此. 本文,为大家揭示 Java学习的套路 背景 Java高并发.分布式的中间件非常多,网上也有很多组件的源码视频.原理视频,汗牛塞屋了. 作为 ...
- select poll epoll Linux高并发网络编程模型
0 发展历程 同步阻塞迭代模型-->多进程并发模型-->多线程并发模型-->select-->poll-->epoll-->... 1 同步阻塞迭代模型 bind( ...
- 尼恩 Java高并发三部曲 [官方]
高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三部曲 > 面试必备 + 大厂必备 + 涨薪 ...
随机推荐
- 自从学会了 Array.reduce() ,再也离不开它
(转载)原文链接:https://juejin.im/post/5dfd9d27e51d455825129ec3 在所有后 ES6 时代的数组方法中,我觉得最难理解的就是Array.reduce( ...
- 1、selenium 8大元素定位方式
元素定位方式: id name css class_name tag_name partial_link link_text : driver. find_element_by_link_text(& ...
- sublime-1
1.提示找不到margo go get github.com/DisposaBoy/MarGo(该工具已经被作者清空了,大部分人在这一步就被卡住了) 如果你也是在第二步卡住了,那么可以按照我的方法进行 ...
- sql中的(case when then else end )的用法(相当于java中的if else)
Case具有两种格式:简单Case函数和Case搜索函数. 1.简单Case函数: CASE sex WHEN‘1’THEN‘男’ WHEN‘0’THEN‘女’ ELSE‘其他’END 2.Case搜 ...
- Python--字符编码、文字处理、函数
了解字符编码的知识储备 我们日常用到的文本编辑器有nodepad++,pycharm,word等等,用他们存取文件的过程大致类似,需要知道打开编辑器就打开了启动了一个进程,是在内存中的,所以在编辑器编 ...
- ofbiz保存jsp页面数据
1.前台js保存 <script type="text/javascript" src="/ecloud/js/js/jquery.min.js"> ...
- USB驱动程序设计
1.USB驱动模型 ①USB HOST控制器驱动 ②USBhexin ③USB客户端驱动 设备4个层次:设备(device).配置(Config).接口(Interface).端点(Endpoint) ...
- Service系统服务(三):查看进程信息、进程调度及终止、系统日志分析、使用systemctl工具
一.查看进程信息 目标: 本例要求掌握查看进程信息的操作,使用必要的命令工具完成下列任务: 找出进程 gdm 的 PID 编号值 列出由进程 gdm 开始的子进程树结构信息 找出进程 sshd 的父进 ...
- Mysql忘记密码:关于ERROR 1045 (28000): Access denied for user 'ODBC'@'localhost' (using password: NO)的问题
命令行登录mysql时,出现ERROR 1045 (28000): Access denied for user 'ODBC'@'localhost' (using password: NO)的提示. ...
- zabbix历史数据全部清楚
#这种方法会出现 监控项不可用的情况 1.停掉zabbix_server 2.重重名表 RENAME TABLE history to history_20180117; RENAME TABLE h ...