【Java基础】Java网络编程基础知识
什么是网络编程
网络编程是通过使用套接字来达到进程间通信目的,那什么是套接字呢?其实套接字是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程,具体来看,套接字=IP地址+TCP/UDP + 端口的组合。
网络编程的三要素
网络编程中,通信“双方”要如何找到彼此呢?互联网是通过IP地址来区分上网电脑的,但是通信的“双方”都是物理电脑上跑的进程,在一台电脑上通过端口来区分不同进程和程序,最后,在互联网通信中,我们分UDP和TCP两种协议。所以网络编程的三要素就是:IP地址,端口号,通信协议。
IP地址
IP地址是指互联网协议地址,IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。IP地址编址方案将IP地址空间划分为A、B、C、D、E五类,其中A、B、C是基本类,D、E类作为多播和保留使用。IP地址总共是4个字节,32比特位。
A类:网络号为1个字节,定义最高比特为0,余下7比特为网络号;
B类:网络号为2字节,定义最高比特为10,余下14比特为网络号;
C类:网络号为3字节,定义最高三比特为110,余下21比特为网络号;
D类:不分网络号和主机号,定义最高四比特为1110,表示一个多播地址,即多目的地传输,可用来识别一组主机;
E类:以1111开始,为将来使用保留。E类地址保留,仅作实验和开发用;
另外,全0类地址0.0.0.0表示任意网络。全1的IP地址255.255.255.255是当前子网的广播地址。网络号127。TCP/IP协议规定网络号127不可用于任何网络。其中有一个特别地址:127.0.0.1称之为回送地址(Loopback),它将信息通过自身的接口发送后返回,可用来测试端口状态。
总之:1~126属A类地址,128~191属B类地址,192~223属C类地址,224~239属D类地址。除了以上四类地址外,还有E类地址,但暂未使用。
端口号
端口包括物理端口和逻辑端口。物理端口是用于连接物理设备之间的接口,逻辑端口是逻辑上用于区分服务的端口。TCP/IP协议中的端口就是逻辑端口,通过不同的逻辑端口来区分不同的服务。一个IP地址的端口通过16bit进行编号,最多可以有65536个端口。端口是通过端口号来标记的,端口号只有整数,范围是从0 到65535。
协议TCP/UDP--指网络传输层的协议
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,TCP是因特网中的传输层协议,使用三次握手协议建立连接。当主动方发出SYN连接请求后,等待对方回答SYN+ACK,并最终对对方的 SYN 执行 ACK 确认。
UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection,开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。UDP同样也是传输层的协议,但是由于UDP是无连接的协议,所以没有三次握手过程。
Java中网络编程的几个重要类
Java为网络编程提供了专门的包java.net。下面来看看实现基础网络通信的几个类,也是我们上面总结的几个网络编程要素,除了这些要素外,传输的数据在网络中需要打包等。
- InetAddress 此类表示互联网协议 (IP) 地址。
- DatagramSocket 此类表示用来发送和接收数据报包的套接字。
- ServerSocket 此类实现服务器套接字。
- Socket 此类实现客户端套接字(也可以就叫“套接字”)。
- DatagramPacket 此类表示数据报包。
UDP网络编程
用UDP协议进行通信,简单的发送数据按照如下4步走:
A. 创建发送端Socket对象DatagramSocket
B. 创建数据,并把数据打包成DatagramPacket
C. 调用Socket对象的发送方法发送数据包
D. 释放资源
接收数据按如下5步走:
A:创建接收端Socket对象DatagramSocket
B:创建一个数据包(接收容器)
C:调用Socket对象的接收方法接收数据包DatagramPacket
D:解析数据包,并显示在控制台
E:释放资源
下面写一个简单的示例程序,先写服务端的接收数据的程序:
- public class ReceiveServer {
- public static void main(String[] args) throws IOException {
- // 创建接收端的Socket对象
- DatagramSocket ds = new DatagramSocket(12345);
- // 创建一个数据包用来接收数据
- byte[] bys = new byte[1024];
- DatagramPacket dp = new DatagramPacket(bys, bys.length);
- // 接收数据
- ds.receive(dp);
- // 解析数据
- String ipAdd = dp.getAddress().getHostAddress();
- String str = new String(dp.getData(), 0, dp.getLength());
- System.out.println("地址 " + ipAdd + " 传输的数据为 : " + str);
- // 释放资源
- ds.close();
- }
- }
客户端的发送程序为:
- public class SendClient {
- public static void main(String[] args) throws IOException {
- // 创建发送端的Socket对象
- DatagramSocket ds = new DatagramSocket();
- // 创建数据并打包
- byte[] bys = "这是UDP传输测试".getBytes();
- DatagramPacket dp = new DatagramPacket(bys, bys.length,
- InetAddress.getByName("192.168.1.101"), 12345);
- // 发送数据
- ds.send(dp);
- // 释放资源
- ds.close();
- }
- }
先启动服务端,再启动客户端,最后服务端输出客户端传来的信息:地址 192.168.1.101 传输的数据为 : 这是UDP传输测试
如果多次启动客户端,则会报错端口被占用。
上面的程序虽然实现了基本的通信,但是却有一些问题:
A: 客户端只能传输一条数据,我们希望可以传输多条或者不想传输后再关闭客户端释放资源。
B:服务端和客户端只能接受单线程
为此,将代码改进为键盘录入的多线程版本:
- public class ChatRoom {
- public static void main(String[] args) throws IOException {
- DatagramSocket dsSend = new DatagramSocket();
- DatagramSocket dsReceive = new DatagramSocket(12346);
- SendThread st = new SendThread(dsSend);
- ReceiveThread rt = new ReceiveThread(dsReceive);
- Thread t1 = new Thread(st);
- Thread t2 = new Thread(rt);
- t1.start();
- t2.start();
- }
- }
- class ReceiveThread implements Runnable {
- private DatagramSocket ds;
- public ReceiveThread(DatagramSocket ds) {
- this.ds = ds;
- }
- @Override
- public void run() {
- try {
- while (true) {
- // 创建一个数据包
- byte[] bys = new byte[1024];
- DatagramPacket dp = new DatagramPacket(bys, bys.length);
- // 接收数据
- ds.receive(dp);
- // 解析数据
- String ip = dp.getAddress().getHostAddress();
- String s = new String(dp.getData(), 0, dp.getLength());
- System.out.println("地址: " + ip + " 数据: " + s);
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- class SendThread implements Runnable {
- private DatagramSocket ds;
- public SendThread(DatagramSocket ds) {
- this.ds = ds;
- }
- @Override
- public void run() {
- try {
- // 封装键盘录入数据
- BufferedReader br = new BufferedReader(new InputStreamReader(
- System.in));
- String line = null;
- while ((line = br.readLine()) != null) {
- if ("bye".equals(line)) {
- break;
- }
- // 创建数据并打包
- byte[] bys = line.getBytes();
- DatagramPacket dp = new DatagramPacket(bys, bys.length,
- InetAddress.getByName("192.168.1.101"), 12346);
- // 发送数据
- ds.send(dp);
- }
- // 释放资源
- ds.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
TCP网络编程
TCP网络编程是面向连接的,用到的套接字是Socket和ServerSocket,其中Socket是客户端用的套接字,其构造方法如下:
- Socket()
- 通过系统默认类型的 SocketImpl 创建未连接套接字
- Socket(InetAddress address, int port)
- 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
- Socket(InetAddress host, int port, boolean stream)
- 已过时。 Use DatagramSocket instead for UDP transport.
- Socket(InetAddress address, int port, InetAddress localAddr, int localPort)
- 创建一个套接字并将其连接到指定远程地址上的指定远程端口。
- Socket(Proxy proxy)
- 创建一个未连接的套接字并指定代理类型(如果有),该代理不管其他设置如何都应被使用。
- protected Socket(SocketImpl impl)
- 使用用户指定的 SocketImpl 创建一个未连接 Socket。
- Socket(String host, int port)
- 创建一个流套接字并将其连接到指定主机上的指定端口号。
- Socket(String host, int port, boolean stream)
- 已过时。 使用 DatagramSocket 取代 UDP 传输。
- Socket(String host, int port, InetAddress localAddr, int localPort)
- 创建一个套接字并将其连接到指定远程主机上的指定远程端口。
常用的方法如下:
- void close()
- 关闭此套接字。
- void connect(SocketAddress endpoint)
- 将此套接字连接到服务器。
- InetAddress getInetAddress()
- 返回套接字连接的地址。
- InputStream getInputStream()
- 返回此套接字的输入流。
- OutputStream getOutputStream()
- 返回此套接字的输出流。
- int getPort()
- 返回此套接字连接到的远程端口。
- SocketAddress getRemoteSocketAddress()
- 返回此套接字连接的端点的地址,如果未连接则返回 null。
- void shutdownInput()
- 此套接字的输入流置于“流的末尾”。
- void shutdownOutput()
- 禁用此套接字的输出流。
ServerSocket套接字是服务器端的套接字,服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。其构造方法如下:
- ServerSocket()
- 创建非绑定服务器套接字。
- ServerSocket(int port)
- 创建绑定到特定端口的服务器套接字。
- ServerSocket(int port, int backlog)
- 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。
- ServerSocket(int port, int backlog, InetAddress bindAddr)
- 使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。
常用方法如下:
- Socket accept()
- 侦听并接受到此套接字的连接。
- void bind(SocketAddress endpoint)
- 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。
- void bind(SocketAddress endpoint, int backlog)
- 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。
- void close()
- 关闭此套接字。
服务器端主要是获取连接上的客户端Socket,然后接可以获取对应的输入输出流进行读取和写入。
另外,必须先启动服务器,因为TCP是面向连接的,要进行三次握手取得连接后才可以开始写或者读数据。
下面给出一个客户端上传文件的多线程例子:
- public class UploadClient {
- public static void main(String[] args) throws IOException {
- // 创建客户端Socket对象
- Socket s = new Socket("192.168.1.101", 12121);
- // 封装文本文件
- BufferedReader br = new BufferedReader(new FileReader(
- "UploadClient.java"));
- // 封装通道内流
- BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
- s.getOutputStream()));
- String line = null;
- while ((line = br.readLine()) != null) { // 阻塞读
- bw.write(line);
- bw.newLine();
- bw.flush();//刷新
- }
- // Socket提供了一个终止,它会通知服务器你别等了,我没有数据过来了
- s.shutdownOutput();
- // 接收反馈
- BufferedReader brClient = new BufferedReader(new InputStreamReader(
- s.getInputStream()));
- String client = brClient.readLine(); // 阻塞
- System.out.println(client);
- // 释放资源
- br.close();
- s.close();
- }
- }
- public class UploadServer {
- public static void main(String[] args) throws IOException {
- // 创建服务器Socket对象
- ServerSocket ss = new ServerSocket(12121);
- while (true) {
- Socket s = ss.accept();//每接受一个Socket则新建一个对应的线程处理
- new Thread(new UserThread(s)).start();
- }
- }
- }
- public class UserThread implements Runnable {
- //Socket对象
- private Socket s;
- public UserThread(Socket s) {
- this.s = s;
- }
- @Override
- public void run() {
- BufferedReader br = null;
- BufferedWriter bw = null;
- try {
- // 封装通道内的流
- br = new BufferedReader(new InputStreamReader(
- s.getInputStream()));
- // 封装文本文件,为了防止名称冲突加上当前时间
- String newName = System.currentTimeMillis() + ".txt";
- bw = new BufferedWriter(new FileWriter(newName));
- String line = null;
- while ((line = br.readLine()) != null) { // 阻塞读
- bw.write(line);
- bw.newLine();
- bw.flush();//刷新
- }
- // 给出反馈,客户端可以终止
- BufferedWriter bwServer = new BufferedWriter(
- new OutputStreamWriter(s.getOutputStream()));
- bwServer.write("文件上传成功");
- bwServer.newLine();
- bwServer.flush();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- // 释放资源
- if(bw!=null){
- try {
- bw.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- if(s!=null){
- try {
- s.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
【Java基础】Java网络编程基础知识的更多相关文章
- 大数据学习笔记——Java篇之网络编程基础
Java网络编程学习笔记 1. 网络编程基础知识 1.1 网络分层图 网络分层分为两种模型:OSI模型以及TCP/IP网络模型,前者模型分为7层,是一个理论的,参考的模型:后者为实际应用的模型,具体对 ...
- Java SE之网络编程:知识框架
- java第九节 网络编程的基础知识
/** * * 网络编程的基础知识 * 网络协议与TCP/IP * IP地址和Port(端口号) * 本地回路的IP地址:127.0.0.1 * 端口号的范围为0-65535之间,0-1023之间的端 ...
- Java网络编程基础(Netty预备知识)
今天在家休息,闲来无事,写篇博客,陶冶下情操~~~ =================我是分割线================ 最近在重新学习Java网络编程基础,以便后续进行Netty的学习. 整 ...
- Java网络编程和NIO详解开篇:Java网络编程基础
Java网络编程和NIO详解开篇:Java网络编程基础 计算机网络编程基础 转自:https://mp.weixin.qq.com/s/XXMz5uAFSsPdg38bth2jAA 我们是幸运的,因为 ...
- 黑马程序员:Java基础总结----网络编程
黑马程序员:Java基础总结 网络编程 ASP.Net+Android+IO开发 . .Net培训 .期待与您交流! 网络编程 网络通讯要素 . IP地址 . 网络中设备的标识 . 不易记忆,可用 ...
- java网络编程基础——网络基础
java网络编程 网络编程基础 1.常用的网络拓扑结构: 星型网络.总线网络.环线网络.树形网络.星型环线网络 2.通信协议的组成 通信协议通常由3部分组成: 语义部分:用于决定通信双方对话类型 语法 ...
- java网络编程基础
前言 通过网络进行数据传输时,一般使用TCP/UDP进行数据传输.但是两个的区别就是TCP可靠,UDP不可靠.两个的共同之处就是都需要建立socket套接字,将IP地址和端口port进行绑定.但是服务 ...
- [Java入门笔记] 面向对象编程基础(二):方法详解
什么是方法? 简介 在上一篇的blog中,我们知道了方法是类中的一个组成部分,是类或对象的行为特征的抽象. 无论是从语法和功能上来看,方法都有点类似与函数.但是,方法与传统的函数还是有着不同之处: 在 ...
- C#网络编程基础知识
C#网络编程基础知识一 1.IPAddress类 用于表示一个IP地址.IPAddress默认构造函数 public IPAddress(long address);一般不用 其中Parse()方法最 ...
随机推荐
- OD之常用命令
一:od断点注释保存的问题,由于od只有在正常退出的情况下才会保存分析代码时留下的注释,而很多时候为了在退出od时不让目标程序退出使用了剥离进程,这样就会导致这次操作所有的注释都没有保存,第二次重新载 ...
- OneAPM 技术公开课:北京,北京!
随着互联网行业的高速发展,数据库已经是绝大多数 IT 应用的核心因素,虽然数据库种类繁多,但是多层体系结构以及 SOA 的发展,使得应用逻辑的实现前移.数据库的性能与其功能相比较,变得越来越重要了. ...
- 一个UUID生成算法的C语言实现 --- WIN32版本 .
一个UUID生成算法的C语言实现——WIN32版本 cheungmine 2007-9-16 根据定义,UUID(Universally Unique IDentifier,也称GUID)在时 ...
- 用户自定义结构数据与VARIANT转换 .
用户自定义结构数据与VARIANT转换 cheungmine 将用户自定义的C结构数据存储成VARIANT类型,需要时再将VARIANT类型转为用户自定义的结构数据,有十分现实的意义,既然我们不想为这 ...
- ubuntu下如何设置主机名
方法如下: 在终端输入 hostname 查看主机名主机名存放在/etc/hostname中 ,sudo gedit /etc/hostname 修改后保存/etc/hosts 还有一份 , sudo ...
- Ado.Net小练习01(数据库文件导出,导入)
数据库文件导出主要程序: <span style="font-family: Arial, Helvetica, sans-serif;"><span style ...
- MyBatis的CRUD操作
MyBatis的两个主要配置文件 mytatis.xml:放在src目录下,常见的配置如下 <?xml version="1.0" encoding="UTF-8& ...
- 两页pdf打印为一页,并且放大(打印英文pdf常用)
多很英文书籍都是小书,若我们直接打印它的pdf会很厚,比如我要打印一本 thinking in C++,就要800+页.不如把两页打成一页.但是打成一页之后又太小了,需要放大.具体方法如下: 前提 ...
- nyist 510昂贵的聘礼
/* 好好的图论题啊,最短路的应用,dijkstra算法 */ #include <iostream> using namespace std; const int INF=100000; ...
- CCS学习资料汇总
2013-06-20 10:31:52 CSDN无网无痕的专栏,有比较多的关于DSP的文章: http://blog.csdn.net/zzsfqiuyigui/article/details/694 ...