计算机网络
定义
(连接分散的计算机设备以实现信息传递的系统)
计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统、网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统
组成
- 计算机网络通俗地讲就是由多台计算机(或其它计算机网络设备)通过传输介质和软件物理(或逻辑)连接在一起组成的。总的来说计算机网络的组成基本上包括:计算机、网络操作系统、传输介质(可以是有形的,也可以是无形的,如无线网络的传输介质就是空间)以及相应的应用软件四部分。
功能
- 数据通信
数据通信是计算机网络的最主要的功能之一。数据通信是依照一定的通信协议,利用数据传输技术在两个终端之间传递数据信息的一种通信方式和通信业务。 - 资源共享
资源共享是人们建立计算机网络的主要目的之一。计算机资源包括硬件资源、软件资源和数据资源。硬件资源的共享可以提高设备的利用率,避免设备的重复投资。 - 集中管理
计算机网络技术的发展和应用,已使得现代的办公手段、经营管理等发生了变化。目前,已经有了许多管理信息系统、办公自动化系统等,通过这些系统可以实现日常工作的集中管理,提高工作效率,增加经济效益。 - 实现分布式处理
网络技术的发展,使得分布式计算成为可能。对于大型的课题,可以分为许许多多小题目,由不同的计算机分别完成,然后再集中起来,解决问题。 - 负荷均衡
荷均衡是指工作被均匀的分配给网络上的各台计算机系统。网络控制中心负责分配和检测,当某台计算机负荷过重时,系统会自动转移负荷到较轻的计算机系统去处理。
计算机网络通信
三要素
- IP:电子设备(计算机)在网络中的唯一标识。
- 端口:应用程序在计算机中的唯一标识。
- 传输协议:统一规定的传输规则。
基本协议
OSI和TCP/IP
OSI 七层模型和TCP/IP模型及对应协议(详解)
IP地址
IP地址(Internet Protocol Address)是指互联网协议地址,又译为网际协议地址。
- IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。
IP协议
IP协议是为计算机网络相互连接进行通信而设计的协议。在因特网中,它是能使连接到网上的所有计算机网络实现相互通信的一套规则,规定了计算机在因特网上进行通信时应当遵守的规则。任何厂家生产的计算机系统,只要遵守IP协议就可以与因特网互连互通。
IP地址
IP协议中还有一个非常重要的内容,那就是给因特网上的每台计算机和其它设备都规定了一个唯一的地址,叫做“IP地址”。由于有这种唯一的地址,才保证了用户在连网的计算机上操作时,能够高效而且方便地从千千万万台计算机中选出自己所需的对象来。
IP地址类型
- 公有地址
公有地址(Public address)由Inter NIC(Internet Network Information Center因特网信息中心)负责。这些IP地址分配给注册并向Inter NIC提出申请的组织机构。通过它直接访问因特网。 - 私有地址
私有地址(Private address)属于非注册地址,专门为组织机构内部使用。
以下列出留用的内部私有地址
端口
定义
"端口"是英文port的意译,可以认为是设备与外界通讯交流的出口。端口可分为虚拟端口和物理端口,其中虚拟端口指计算机内部或交换机路由器内的端口,不可见。例如计算机中的80端口、21端口、23端口等。物理端口又称为接口,是可见端口,计算机背板的RJ45网口,交换机路由器集线器等RJ45端口。电话使用RJ11插口也属于物理端口的范畴。
端口分类
按端口号可分为3大类:
(1)公认端口(Well Known Ports):从0到1023,它们紧密绑定(binding)于一些服务。通常这些端口的通讯明确表明了某种服务的协议。例如:80端口实际上总是HTTP通讯。
(2)注册端口(Registered Ports):从1024到49151。它们松散地绑定于一些服务。也就是说有许多服务绑定于这些端口,这些端口同样用于许多其它目的。例如:许多系统处理动态端口从1024左右开始。
(3)动态和/或私有端口(Dynamic and/or Private Ports):从49152到65535。理论上,不应为服务分配这些端口。实际上,机器通常从1024起分配动态端口。但也有例外:SUN的RPC端口从32768开始。
网络通信协议
- 同一网络中的计算机进行连接和通信的规则。
- 目前应用最广泛的是TCP/IP协议(包括IP协议、TCP协议、UDP协议、ICMP协议等)。
- 在进行数据传输时,要求发送的数据与接收到的数据完全一样,这时,就需要在原有数据上添加很多信息,以保证数据在传输过程中数据式完全一致。TCP/IP协议的层次分为4层。
- 应用层–如 HTTP、FTP、DNS
主要负责应用程序的协议,如HTTP协议,FTP协议。 - 传输层–如 TCP、UDP
主要使网络程序进行通信,在进行网络通信时,可以采用TCP协 议,也可以采用UDP协议。 - 网络层– 如 IP协议,ICMP、IGMP
整个TCP/IP协议的核心,它主要用于将传输的数据进行分组, 将分组数据发送到目标计算机或者网络。 - 链路层– 如 驱动程序,接口
用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤,双绞线的驱动。
TCP
TCP实现聊天
客户端
- 连接服务器Socket
- 发送消息 IO流
package com.hello;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
//客户端
public class TcpClientDemo1 {
public static void main(String[] args) {
Socket socket = null;
OutputStream os = null;
try {
//1.要知道服务器的地址,创建端口号
InetAddress serverIP= InetAddress.getByName("127.0.0.1");
int part = 9999;
//2.创建一个Socket连接
socket = new Socket(serverIP,part);
//3.发送消息 IO流
os = socket.getOutputStream();
os.write("欢迎学习TCP".getBytes());
} catch (Exception e) {
e.printStackTrace();
}finally {
//关闭资源
if (os!=null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
服务端
- 建立服务的端口ServerSocket
- 等待用户的连接 accept
- 接收用户的消息
package com.hello;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
//服务端
public class TcpServerDemo1 {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
//1.我们得有一个地址
serverSocket = new ServerSocket(9999);
while (true){
//2.等待客户端连接过来
socket = serverSocket.accept();
//3.读取客户端的消息
is = socket.getInputStream();
//3.管道流
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len=is.read(buffer))!=-1){
baos.write(buffer,0,len);
}
System.out.println(baos.toString());
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭资源
if (baos!=null){
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (serverSocket!=null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
TCP文件上传实现
客户端
package com.hello;
//客户端2
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class TcpClientDemo2 {
public static void main(String[] args) throws Exception {
//1.创建一个Socket连接
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000);
//2.创建一个输出流
OutputStream os = socket.getOutputStream();
//3.读取文件
FileInputStream fis = new FileInputStream(new File("001.jpg"));
//4.写出文件
byte[] buf = new byte[1024];
int len;
while ((len = fis.read(buf)) != -1) {
os.write(buf, 0, len);
}
//通知服务器,我已经结束了
socket.shutdownOutput();//我已经传输完了
//确定服务器接收完毕,才能够断开连接
InputStream inputStream = socket.getInputStream();
//String byte[]
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf2 = new byte[1024];
int len2;
while ((len2 = inputStream.read(buf2)) != -1) {
baos.write(buf2, 0, len2);
}
System.out.println(baos.toString());
//5.关闭资源,先开后关
baos.close();
inputStream.close();
fis.close();
os.close();
socket.close();
}
}
服务端
package com.hello;
//服务端2
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServerDemo2 {
public static void main(String[] args) throws Exception {
//1.创建服务
ServerSocket serverSocket = new ServerSocket(9000);
//2.监听客户端的连接
Socket socket = serverSocket.accept();//阻塞式监听,会一直监听(等待客户端连接)
//3.获取输入流
InputStream is = socket.getInputStream();
//4.文件输出
FileOutputStream fos = new FileOutputStream(new File("receive.jpg"));
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
//通知客户端我接收完毕了
OutputStream os = socket.getOutputStream();
os.write("我接收完毕了,你可以断开了".getBytes());
//关闭资源
fos.close();
is.close();
socket.close();
serverSocket.close();
}
}
运行时先启动服务端再启动客户端
UDP
UDP消息发送
UDP短信发送不需要连接,但需要知道对方的IP地址
客户端
package com.hello;
//客户端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
//不需要连接服务器
public class UdpClientDemo3 {
public static void main(String[] args) throws Exception {
//建立一个Socket
DatagramSocket socket = new DatagramSocket(8080);
//创建包
String msg = "Hello World!";
InetAddress localhost = InetAddress.getByName("localhost");
int port = 9090;
//数据,数据的起始长度,要发送的对象
DatagramPacket packet = new DatagramPacket(msg.getBytes(),0, msg.getBytes().length,localhost, port);
//发送包
socket.send(packet);
//关闭流
socket.close();
}
}
接收端
package com.hello;
//接收端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
//还是要等待客户端连接
public class UdpServerDemo3 {
public static void main(String[] args) throws Exception {
//开放端口
DatagramSocket socket = new DatagramSocket(9090);
//接收数据包
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);//阻塞接收
System.out.println(packet.getAddress().getHostAddress());
System.out.println(new String(packet.getData(),0,packet.getLength()));
//关闭连接
socket.close();
}
}
运行时先启动接收端等待客户端连接再启动客户端
UDP多线程在线聊天
一般的聊天程序由于追求快捷的数据传输速度,而又不是比较关注数据的完整性,都是用UDP协议来传递数据,
而且聊天程序在发送信息的时候,也可以同时进行信息的接收功能,就好像QQ一样,我们可以在下面的输入框慢慢打字发信息,但是上面的信息框却是在同时接收信息,
要在一个程序里面实现这种功能,就要用到了多线程了,其中一个线程用来专门接收数据,一个纯种用来专门发送数据,像QQ一样的估计还有线程专门用来处理视频和文件传输等。
下面利用JAVA的多线程和UDP网络功能实现一个简单的聊天程序,其中主要涉及到四个类,一个是接收信息的类,一个是发送信息的类,这两个类我们到时在主函数中用多线程来执行,
还有两个类,也就是两个具有main函数的聊天窗口程序,两个简单的控制台程序。
package com.hello;
import java.io.*;
import java.net.*;
//发送的类
class Send implements Runnable
{
private DatagramSocket ds;
private int recePort;// 接收端的端口。即要把数据发送到那个端口
public Send(DatagramSocket ds, int port)
{
this.ds = ds;
this.recePort = port;
}
public void run()
{
try
{
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while ((line = bufr.readLine()) != null)
{
if ("886".equals(line))
{
break;
}
byte[] buf = line.getBytes();
// 我们把数据发往目标端口,
DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("127.0.0.1"), this.recePort);
ds.send(dp);
}
} catch (Exception e)
{
throw new RuntimeException("send failure");
}
}
}
class Rece implements Runnable
{
private DatagramSocket ds;
public Rece(DatagramSocket ds)
{
this.ds = ds;
}
public void run()
{
try
{
while (true)
{
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
ds.receive(dp);
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String data = new String(dp.getData(), 0, dp.getLength());
System.out.println("来自ip为:" + ip + " 端口为:" + port + "的信息为:" + data);
}
} catch (Exception e)
{
throw new RuntimeException("Rece failure");
}
}
}
class ChatDemoA
{
public static void main(String[] args) throws Exception
{
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket receSocket = new DatagramSocket(10000);// 侦听10000端口
new Thread(new Send(sendSocket, 10001)).start();// 把数据发往10001端口
new Thread(new Rece(receSocket)).start();
}
}
class ChatDemoB
{
public static void main(String[] args) throws Exception
{
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket receSocket = new DatagramSocket(10001);// 侦听10001端口
new Thread(new Send(sendSocket, 10000)).start();// 把数据发住10000端口
new Thread(new Rece(receSocket)).start();
}
}
[拓展]
- 每个编译单元(文件)都只能有一个public类。即每个编译单元都有单一的公共接口,用public类实现。此时,mian()就必须要包含在public类中。
- public类的名称必须完全与含有该编译单元的文件名称一致,包括大小写。如果不匹配,编译时错误。
- 如果编译单元(文件)中不含有一个public类,此时编译单元文件名称可以与启动类名称可以不一致,即可以随意对文件命名。这样的话,main()不是必须要放在public类中才能运行程序。
- 总的来说,一个Java源文件中最多只能有一个public类,当有一个public类时,源文件名必须与之一致,否则无法编译,如果源文件中没有一个public类,则文件名与类中没有一致性要求。至于main()不是必须要放在public类中才能运行程序。