计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统(https://baike.baidu.com/item/网络操作系统/3997),网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
让计算机与计算机之间建立连接、通信
传播交流信息
数据交换
通信
? javaweb开发:网页编程 B/S架构(是通过浏览器去访问)
? 网络编程:TCP/IP 客户端 C/S结构(比如登录qq这些)
TCP/IP参考模型:
重点关注传输层的TCP和UDP
该协议为网络层协议
负责数据从一台机器发送到另外一台机器
给互联网每台设备分配唯一标识(IP地址)
可以唯一定位一台网络上的计算机
127.0.0.1是本机地址
ip地址的分类
公网(互联网)/私网(局域网)
总结上网流程:
我们能够上网靠的是isp组织分给我们的Ip地址,但是这个ip地址一般不是给个人的,一般都是给一个单位,一个区域的,也就是说我们实际上能接触到的一般都是私有地址,即我们用ipconig查到的都是私有地址,也就相当于局域网内的ip地址,当我们真正联网时,会先把数据发送到路由,然后再由路由进行处理实现真正的联网操作,路由的地址才是真正联网的Ip地址,也就是pubilc ip,而我们在自己电脑上查到的都是私有ip
域名-----域名的诞生是为了解决记忆IP问题
端口表示计算机上的一个程序的进程,端口号是在通信实体上进行网络通讯程序的唯一标识
不同的进程有不同的端口号!!!用来区分软件
端口被规定有0~65535个端口
单个协议下面,端口号不能冲突(tcp,udp各有65535个端口 ,不同协议下,端口号可以相同)
端口分类
动态、私有:49152~65535
//测试端口
public class demo03 {
public static void main(String[] args) {
InetSocketAddress isa1=new InetSocketAddress("127.0.0.1",8080);
InetSocketAddress isa2=new InetSocketAddress("localhost",8080);
InetSocketAddress isa3=new InetSocketAddress("www.baidu.com",8080);
System.out.println(isa1);
System.out.println(isa2);
System.out.println(isa3);//得到主机名+ip地址+端口
System.out.println("==============================================================");
System.out.println(isa1.getHostName());//得到主机名
System.out.println(isa3.getHostName());//得到主机名
System.out.println(isa1.getAddress());//得到ip地址
System.out.println(isa3.getAddress());//得到ip地址
System.out.println(isa1.getPort());//得到端口号
System.out.println(isa3.getPort());//得到端口号
}
}
/*
/127.0.0.1:8080
localhost/127.0.0.1:8080
www.baidu.com/183.232.231.172:8080
==============================================================
127.0.0.1
www.baidu.com
127.0.0.1/127.0.0.1
www.baidu.com/183.232.231.172
8080
8080
UDP协议和TCP协议都是传输层协议。
TCP
建立连接的过程需要:TCP三次握手
这样3次握手就完成了,主机A和主机B 就可以传输数据了。
断开连接的过程需要:TCP四次挥手
//A:B 啊,我不想玩了
//B:哦,你不想玩了啊,我知道了
这个时候,只是 A 不想玩了,即不再发送数据,但是 B 可能还有未发送完的数据,所以需要等待 B 也主动关闭。
//B:A 啊,好吧,我也不玩了,拜拜
//A:好的,拜拜
序列号 确认应答 超时重传 拥塞控制
创建一个TCP的socket, 同时在内核中创建一个发送缓冲区和一个接收缓冲区;
另一方面, TCP的一个连接, 既有发送缓冲区, 也有接收缓冲区, 那么对于这一个连接, 既可以读数据, 也可以写数据. 这个概念叫做 全双工 。
① 调用write时, 数据会先写入发送缓冲区中;
② 如果发送的字节数太长, 会被拆分成多个TCP的数据包发出; 如果发送的字节数太短, 就会先在缓冲区里等待, 等到缓冲区长度差不多了, 或者其他合适的时机发送出去;
③ 接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区;
④ 然后应用程序可以调用read从接收缓冲区拿数据;
① 首先要明确, 粘包问题中的 “包” , 是指的应用层的数据包;
② 在TCP的协议头中, 没有如同UDP一样的 “报文长度” 这样的字段, 但是有一个序号这样的字段;
③ 站在传输层的角度, TCP是一个一个报文过来的,按照序号排好序放在缓冲区中;
④ 站在应用层的角度, 看到的只是一串连续的字节数据. 那么应用程序看到了这么一连串的字节数据, 就不知道从哪个部分开始到哪个部分是一个完整的应用层数据包。
UDP
TCP和UDP的报头不同
TCP和UDP的协议不同
//测试IP
//假设我电脑的ip地址为192.168.101.47
public class demo01 {
public static void main(String[] args) {
try {
//1.创建本机ip地址对象
//1.使用getLocalHost()方法
InetAddress ia1=InetAddress.getLocalHost();
//通过主机的地址返回ip地址对象
System.out.println("ip地址:"+ia1.getHostAddress()+"主机名:"+ia1.getHostName());
System.out.println(ia1);
System.out.println("======================================");
//2.使用getByName("ip地址/主机名")方法
InetAddress ia2=InetAddress.getByName("LAPTOP-2AV957FM");
//通过主机的名字返回ip地址对象
InetAddress ia3=InetAddress.getByName("192.168.101.47");
//通过主机的地址返回ip地址对象
System.out.println("ip地址:"+ia2.getHostAddress()+"主机名:"+ia2.getHostName());
System.out.println("ip地址:"+ia3.getHostAddress()+"主机名:"+ia3.getHostName());
System.out.println(ia2);
System.out.println(ia3);
System.out.println("======================================");
//3.使用getByName("127.0.0.1")方法
InetAddress ia4=InetAddress.getByName("127.0.0.1");
//通过(主机的地址/主机名)返回ip地址对象
System.out.println("ip地址:"+ia4.getHostAddress()+"主机名:"+ia4.getHostName());
System.out.println(ia4);
System.out.println("======================================");
//4.使用getByName("localhost")方法
InetAddress ia5=InetAddress.getByName("localhost");
//通过主机名返回ip地址对象
System.out.println("ip地址:"+ia5.getHostAddress()+"主机名:"+ia5.getHostName());
System.out.println(ia5);
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
/*
结果:
ip地址:192.168.101.47主机名:LAPTOP-2AV957FM
LAPTOP-2AV957FM/192.168.101.47
======================================
ip地址:192.168.101.47主机名:LAPTOP-2AV957FM
ip地址:192.168.101.47主机名:LAPTOP-2AV957FM
LAPTOP-2AV957FM/192.168.101.47
LAPTOP-2AV957FM/192.168.101.47
======================================
ip地址:127.0.0.1主机名:127.0.0.1
127.0.0.1/127.0.0.1
======================================
ip地址:127.0.0.1主机名:localhost
localhost/127.0.0.1
*/
注意:
1. InetAddress(是没有构造器的,只能通过它的静态方法返回这个对象)
2.如果利用主机地址构造ip地址对象,则打印时,是不知道主机名的,只有找到了主机名,才能显示出来
InetAddress ia3=InetAddress.getByName("192.168.101.47");
//通过主机的地址返回ip地址对象
System.out.println("ip地址:"+ia3.getHostAddress()+"主机名:"+ia3.getHostName());
System.out.println(ia3);
/*
结果:
ip地址:192.168.101.47主机名:LAPTOP-2AV957FM
LAPTOP-2AV957FM/192.168.101.47
*/
//若这两句互换位置
System.out.println(ia3);
System.out.println("ip地址:"+ia3.getHostAddress()+"主机名:"+ia3.getHostName());
/*
结果:
/192.168.101.47
ip地址:192.168.101.47主机名:LAPTOP-2AV957FM
*/
//测试IP2
//假设我电脑的ip地址为192.168.101.47
public class demo02 {
public static void main(String[] args) {
try {
//二.创建局域网ip地址对象
InetAddress ia6=InetAddress.getByName("192.168.101.48");
System.out.println("ip地址:"+ia6.getHostAddress()+"主机名:"+ia6.getHostName());
System.out.println("2s钟是否可以找到该地址的对象:"+ia6.isReachable(2000));
//结果为false,因为这个地址是不存在的,没有主机
//三.创建外网ip地址对象
InetAddress ia7=InetAddress.getByName("www.baidu.com");
//通过域名返回ip地址对象
System.out.println("ip地址:"+ia7.getHostAddress()+"主机名:"+ia7.getHostName());
System.out.println("2s钟是否可以找到该地址的对象:"+ia7.isReachable(2000));
//结果为true,因为这个地址是存在的
System.out.println("=========================================");
System.out.println("打印“www.baidu.com”这个域名所有ip地址对象:");
InetAddress[] inetaddresses=InetAddress.getAllByName("www.baidu.com");
for(InetAddress ia:inetaddresses){
System.out.println(ia.getHostAddress());
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
ip地址:192.168.101.48主机名:192.168.101.48
2s钟是否可以找到该地址的对象:false
ip地址:183.232.231.174主机名:www.baidu.com
2s钟是否可以找到该地址的对象:true
=========================================
打印“www.baidu.com”这个域名所有ip地址对象:
183.232.231.174
183.232.231.172
注意:一个域名是可以有多个ip地址对象的
完整的抛出异常写法
//基于TCP协议的服务器开发
public class TcpServer1 {
public static void main(String[] args)throws Exception {
//1.创建ServerSocket(服务器套接字),并指定端口号
ServerSocket serverSocket=new ServerSocket(9876);
//2.调用accept()方法,接收客户端请求,这是一个阻塞方法(如果没有客户端请求,它就会发生堵塞)
System.out.println("服务器已启动");
Socket socket=serverSocket.accept();//该方法返回的是客户端Socket套接字
//3.获取输入流,读取客户端发送请求的数据
InputStream is=socket.getInputStream();
//将字节流转换为字符流,这样可以传输中文,再将其功能进行加强,变为字符缓冲输入流
BufferedReader br=new BufferedReader(new InputStreamReader(is,"utf-8"));
String date=br.readLine();
System.out.println("客户发送的数据为:"+date);
//4.获取输出流,发送数据给客户端
//5.关闭释放资源
br.close();
socket.close();
serverSocket.close();
}
}
//基于TCP客户端开发
public class TcpClient1 {
public static void main(String[] args)throws Exception {
//1.创建Socket(客户端套接字),并指定服务器的地址和端口号
Socket socket=new Socket(InetAddress.getByName("127.0.0.1"),9876);
//2.获取输出流,发送请求数据给服务器
OutputStream os=socket.getOutputStream();
//将字节流转换为字符流,这样可以传输中文,再将其功能进行加强,变为字符缓冲输入流
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(os,"utf-8"));
bw.write("我哭了很累!");
//3.获取输入流,读取服务器回复的数据
//4.关闭释放资源
bw.close();
socket.close();
}
}
先运行服务端,再运行客户端
在服务器端
结果:
服务器已启动
客户发送的数据为:我哭了很累!
public class TcpFileServer1 {
public static void main(String[] args) throws Exception{
//1.创建服务器套接字
ServerSocket serverSocket=new ServerSocket(9999);
System.out.println("服务器已启动。。。");
//2.利用accept()方法,接受客户端的请求
Socket socket=serverSocket.accept();
//3.接收客户端的请求数据,创建输入流
InputStream is=socket.getInputStream();
//4.接收客户端的请求的文件,边读边保存
FileOutputStream fos=new FileOutputStream("D:\\ajava\\3.jpg");
int count=0;
byte[] buf=new byte[1024*4];
while ((count=is.read(buf))!=-1){
fos.write(buf,0,count);
}
//5.从服务端发送响应到客户端,已经传输完成,创建输出流
OutputStream os=socket.getOutputStream();
os.write("服务器接收文件完成".getBytes());
//6.关闭
fos.close();
socket.close();
serverSocket.close();
System.out.println("服务器接收完毕");
}
}
public class TcpFileClient1 {
public static void main(String[] args) throws Exception {
//1.创建客户端套接字
Socket socket =new Socket(InetAddress.getByName("127.0.0.1"),9999);
//2.发送请求数据,创建输出流
OutputStream os=socket.getOutputStream();
//3.发送客户端文件,边读取文件,边发送
FileInputStream fis=new FileInputStream("D:\\ajava\\1.jpg");
byte[] buf=new byte[1024*4];
int count =0;
while((count=fis.read(buf))!=-1){
os.write(buf,0,count);
}
//通知服务器,我已经发送完了
socket.shutdownOutput();
//5.接收服务器传回接收完成的响应,创建输入流
InputStream is=socket.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is,"utf-8"));
String date=br.readLine();
System.out.println(date);
//6.关闭
fis.close();
os.close();
socket.close();
System.out.println("客户端发送完毕");
}
}
先运行服务端,再运行客户端
在客户端
结果:
服务器接收文件完成
客户端发送完毕
在服务器端
结果:
服务器已启动。。。
服务器接收完毕
在D://ajava中成功发送了一个和1.jpg相同的3.jpg
不用进行连接(服务器),只是需要对方地址
没有 客户端和服务端的说法,即可以作为接收端,也可以作为发送端
案例1:基于UDP协议的发送端发送信息,接收端接收信息
//Udp:不需要建立服务器连接
public class UdpSent1 {
public static void main(String[] args) throws Exception{
//1.建立一个Soceket
DatagramSocket socket=new DatagramSocket();
//2.建一个包用来发送数据
String s="你好!!";
//发送给谁
InetAddress ia1=InetAddress.getByName("localhost");
int port=9990;
//数据,数据的起始位置,数据的末位置,发送的ip地址,发送的端口号
DatagramPacket packet=new DatagramPacket(s.getBytes(),0,s.getBytes().length,ia1,port);
//DatagramPacket(数组)
//3.发送包
socket.send(packet);
//4.关闭
socket.close();
}
}
//UDP:等待发送端发来的消息
public class UdpRec1 {
public static void main(String[] args) throws Exception {
//1.创建Socket,来开放发送端要发到的端口
DatagramSocket socket=new DatagramSocket(9990);
//2.新建一个包用来接收数据
byte[] buf=new byte[1024];
DatagramPacket packet=new DatagramPacket(buf,0,buf.length);
//接收
//3.接收包
socket.receive(packet);//阻塞接收
System.out.println(packet);
System.out.println("发送包的地址为::"+packet.getAddress());
System.out.println("发送包的内容为:"+new String(packet.getData()));
//packet.getData()得到的数据为数组,要转为字符串输出
//4.关闭
socket.close();
}
}
先运行发送端,再运行接收端
接收端
结果:
发送包的地址为::/127.0.0.1
发送包的内容为:你好!!
//UDP实现循环发送
public class UdpChat1 {
public static void main(String[] args) throws Exception{
//1.创建Socket
DatagramSocket socket=new DatagramSocket(5555);
//2.创建一个包,用来发送
//首先先准备数据:利用控制台读取System.in
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
//如果有读取的数据,则一直发送
while(true) {
String s = br.readLine();
byte[] datas = s.getBytes();
//创建一个包,写好发送的内容,发送的地址
InetAddress ia = InetAddress.getByName("localhost");
int port = 6666;
DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, ia, port);
//发送包
socket.send(packet);
//如果发送的数据为空,则停止发送
if(s==null){
break;
}
}
//3.关闭
br.close();
socket.close();
}
}
//UDP实现循环接收
public class UdpChat2 {
public static void main(String[] args) throws Exception {
//1.创建Socket,来开放发送端需要发送的端口
DatagramSocket socket = new DatagramSocket(6666);
//2.创建一个包,用来接收
while (true) {
//首先先准备区域,用来接收发送端的数据
byte[] buf = new byte[1024];
//创建一个包,用来接收发送端的包
DatagramPacket packet = new DatagramPacket(buf, 0, buf.length);
//接收包
socket.receive(packet);//阻塞接收
//断开连接
byte[] data = packet.getData();
String stringData =new String(data,0,data.length);
System.out.println(stringData);
if (stringData== null) {
break;
}
}
socket.close();
}
}
先运行发送端,再运行接收端
在发送端的控制台写入:
nihao!hhh
nihao2!
你好
在接收端的控制台出现:
nihao!hhh
nihao2!
你好
原文:https://www.cnblogs.com/lyj-study/p/14456653.html