0913~0915(051~053天 网络编程)

09130915(051053天 网络编程)

每日一狗(田园犬西瓜瓜

在这里插入图片描述

网络编程

课前准备

tcp/ip分层协议栈(七层架构):

在这里插入图片描述

tcp的三握四挥:

三次握手

  • 客户端:我要和你创建连接了,你准备一下吧
  • 服务器:我准备好了,你链接吧
  • 客户端:好的,连接创建确定

四次挥手

  • 客户端:我要断开连接了,你准备一下
  • 服务器:别忙我这还有活没玩,等会
  • 服务器:活干完了,断开连接吧
  • 客户端:好的,断开连接确定

背:.tcp断开链接为什么时四挥,能不能三挥?

因为其中还有一段时间需要将工作干完了才能断开

HTTP1.0和1.1的区别:

tcp和udp的区别:

TCP面向连接的协议UDP 用户数据协报议
是否需要建立连接是,三握四挥传输数据之前源端和终端不建立连接
是否安全
有没有重传机制为保证安全需要重传和对传输数据进行正确性验证
思想就是安全就是快
应用场景像是直播一样对数据的时效性要求很高的数据

1. 啥也不是

URL

URL对象代表统一资源定位器,是指向互联网资源的指针

http协议的标准端口为80 https=http+SSL 加密数据传输的http,标准端口为443

  • URL统一资源指针,可以指定一个具体的资源,例如一个html网页
  • URI统一资源标识符,可以认为为了方便记忆,给URL起的别名
// 创建一个URL对象,用于指代网络中的一个资源,如果网址不合法,则抛出MalformedURLException

URL url = new URL("url");

// 重要方法
url.openConnection()URLConnection//   可以获取输入、输出流
url.openStream()InputStream//   直接获取输入流 

// 不重要的方法:通过URL对象的一些方法可以访问该URL对应的资源:

String getFile()://获取该URL的资源名
String getHost()://获取主机名
String getPath()://获取路径部分
int getPort()://获取端口号 
URL和URLConnection比较

URL:统一资源指针

URLConnection:一种连接

URL url = new URL("https://zhuanlan.zhihu.com/p/24860273");
InputStream is = url.openStream();

URLConnection conn = url.openConnection();
InputStream is1 = conn.getInputStream();
OutputStream os1 = conn.getOutputStream();

URL只能获取输入流,但是URLConnection可以获取输入输出流

TCP编程

TCP

  • 提供可靠的数据通讯
  • 面向连接的端对端的保证可靠传输的协议
  • 可以得到一个顺序的无差错的数据流
  • 建立连接后可以互相通信

UDP

  • 不保证数据的可靠性
  • 协议简单、传输速度块(如音视频传输,这些不需要很高的可靠性,允许偶尔丢包)

Socket

Socket套接字,就是两台主机之间逻辑连接的端点。

TPC协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。Socket本质上就是一组接口,是对TCP/IP协议的封装和应用(程序员层面上)

端口号的范围是0到65536,但是0到1024是为特权服务保留的端口号,我们可以选择任意一个当前没有被其他进程使用的端口。

客户端请求与服务器进行连接的时候,根据服务器的域名或者IP地址,加上端口号,打开一个套接字。当服务器接受连接后,服务器和客户端之间的通信就像输入输出流一样进行操作。

开放端口包括三种:

  • 0.0.0.0:端口号
  • 127.0.0.1:端口号(只供本机访问的端口)
  • 主机ip:端口号

cmd命令查看端口使用情况netstat -an

  • LISTENING是指开放着的,等待连接的
  • ESTABLISHED是正在连接
  • CLOSE_WAIT、TIME_WAIT、SYN_SENT是三次握手四次挥手过程中的某些状态
ServerSocket类

Java.net包中的ServerSocket类用于表示服务器套接字,其主要功能是监听客户端的请求,然后将客户端的请求连接存入队列中,默认请求队列大小是50。

端口扫描

//利用的是ServerSocket在创建时,如果端口已经被占用,则报异常
for(int i=0;i<=65535;i++) { //端口号0表示使用自由端口,实际上是不能建立连接的
 try {
     ServerSocket ss=new ServerSocket(i);
     ss.close();  //finally
 } catch (Exception e) {
     System.out.println("端口"+i+"已经被占用");
 }
}
客户端Socket构造器
Socket( // 
    String host, 
    int port, 
    InetAddress localAddr, 
    int localPort
);

Socket( // InetAddress对象中可以封装一个IP地址和一个主机名
    InetAddress address, 
    int port, 
    InetAddress localAddr, 
    int localPort
);
Socket练习题

**1、**客户端与服务器端相互通讯

// 客户端发送:
来个套餐

// 服务器回复:
欢迎光临
这个套餐没了,明天吧
// 服务器端
// 构建服务器对象
ServerSocket ss = new ServerSocket(9009);
// 请求阻塞
Socket socket = ss.accept();
// 获取连接的输入输出流
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
// 使用输出流构建ps方便输出
PrintStream ps = new PrintStream(os);
// 发送信息
ps.println("欢迎光临");
// 使用输入流构建bf方便读取
BufferedReader bf = new BufferedReader(new InputStreamReader(is));
// 接受并输出信息
String str = bf.readLine();
System.out.println(str);
// 发送信息
ps.println("这个套餐没了,明天吧");
// 关闭资源
bf.close();
ps.close();
socket.close();
ss.close();
// 客户端
// 与指定服务器的指定端口建立连接
Socket socket = new Socket("127.0.0.1", 9009);
// 获取连接的输入输出流
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();

// 读取并显示
BufferedReader bf = new BufferedReader(new InputStreamReader(is));
String str = bf.readLine();
System.out.println(str);
// 发送信息
PrintStream ps = new PrintStream(os);
ps.println("来个套餐");
// 获取返回信息并显示
str = bf.readLine();
System.out.println(str);
// 关闭资源
bf.close();
ps.close();
socket.close();

在这里插入图片描述

2、

3、

简单的Client/Setver程序

在这里插入图片描述

需求:客户端传送字符串给服务区端

**服务器端:**一般会常开一个端口来为客户端提供服务

  1. 创建ServerSocket对象,绑定监听端口
  2. 通过accept方法监听客户端请求
  3. 连接建立后,通过输入流读取客户端发送的请求信息
  4. 通过输出流向客户端发送相应信息
  5. 关闭相应的资源对象
ServerSocket server = new ServerSocket(9999);

Socket socket = server.accept();

InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
ps.println("hello");
BufferedReader br = new BufferedReader(new InputStreamReader(is));

String ss = br.readLine();
System.out.println("Client:" + ss);
// 传输数据为指定数据时返还当前时间数据
if ("hello".equals(ss)) {
    Date now = new Date();
    DateFormat df = new SimpleDateFormat("yyyy-MM-DD-E hh:mm:ss");
    ss = df.format(now);
    ps.println(ss);

}
// 关闭资源
br.close();
ps.close();
socket.close();
server.close();

客户端:

  1. 创建Socket对象,指明需求连接的服务器地址和端口号
  2. 连接建立后,通过输出流向服务器发送请求信息
  3. 通过输入流获取服务器响应的信息
  4. 关闭相应资源对象
Socket socket = new Socket("127.0.0.1", 9999);
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();

// 传输数据
PrintStream ps = new PrintStream(os);
ps.println("hello");
// 使用网络传输的输入流创建缓冲流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String ss = br.readLine(); // 阻塞程序
System.out.println("Server:" + ss);

System.out.println("Setver:" + br.readLine());

// 关闭资源
br.close();
ps.close();
socket.close();

服务器端的编程思想

主线程一直处于阻塞等待状态,如果一旦链接建立成功则启动一个新线程对外提供服务,而主线程继续等待新的链接请求。

public class MyServer {
	public static void main(String[] args) throws Exception {
		ServerSocket server = new ServerSocket(9999);
		while (true) {
			Socket socket = server.accept();
			new MyWorker(socket).start();
		}
	}
}

class MyWorker extends Thread {
	private Socket socket;

	public MyWorker(Socket socket) {
		this.socket = socket;
	}

	@Override
	public void run() {
		BufferedReader br = null;
		PrintStream ps = null;
		try {
			InputStream is = socket.getInputStream();
			OutputStream os = socket.getOutputStream();
			br = new BufferedReader(new InputStreamReader(is));
			ps = new PrintStream(os);
			String ss = br.readLine();
			System.out.println("Client:" + ss);
			if ("hello".equals(ss)) {
				Date now = new Date();
				DateFormat df = new SimpleDateFormat("yyyy-MM-ddE hh:mm:ss");
				ss = df.format(now);
				ps.println(ss);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			if (br != null)
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			if (ps != null)
				ps.close();
			if (socket != null)
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}
}

c/s和b/s区别

  • C/S (Client/Server,客户机/服务器)结构,胖客户端应用,是软件系统体系结构的一种。
    • C/S模式简单地讲就是基于企业内部网络的应用系统,大部分的应用逻辑都集中在客户端中,而一般服务器端只提供数据的存储。
    • 不依赖企业外网环境,即无论企业是否能够上网,都不影响应用。
  • B/S结构(Browser/Server结构)结构即浏览器和服务器结构,瘦客户端应用,主要逻辑集中在服务器端,客户端一般只包含的简单的显示逻辑。
    • 它是随着Internet技术的兴起,对C/S结构的一种变化或者改进的结构。
    • 主要事务逻辑在服务器端Server实现,形成所谓三层3-tier结构。

C/S聊天室的实现

服务器端应用包含多个线程,每个Socket对应一个线程,该线程负责读取Socket对应输入流的数据,并将读到的数据向每个Socket发送一次

每个客户端应该包含两个线程,一个负责读取用户键盘输入并将用户输入数据写入Socket对应的流中,一个负责读取Socket对应输入流的数据并打印显示


3. 粘包

数据包界限不清,先发送一个int值标识接下来的数据的长度,然后你读的时候就读这些,然后去读取下一个

粘包问题主要是由于数据包界限不清,最好的解决办法就是在发送数据包前事先发送一个int型数据,该数据代表将要发送的数据包的大小,这样接收端可以每次触发OP_READ的时候先接受一个int大小的数据段到缓存池中,然后,紧接着读出后续完整的大小的包,这样就会处理掉粘包问题。因channel.read()方法不能给读取数据的大小的参数,所以无手动指定读取数据段的大小。但每次调用channel.read()返回的是实际读取的大小。

怎么处理

粘的时候搞一个分隔符、固定消息长度、TLV格式消息

分隔符

在这里插入图片描述

固定消息长度

在这里插入图片描述

4. UDP网络程序

1、使用DatagramSocket()创建一个数据包套接字。
2、使用DatagramPacket(byte[]buf, int offset, int length, InetAddress address, int port)创建要发送的数据包。
3、使用DatagramSocket类的send()方法数据包

UDP网络通信的收包过程

1、使用DatagramSocket(int)创建一个数据包套接字,绑定到指定的端口。
2、使用DatagramPacket(byte[]buf,int length)创建字节数组来接收数据包.
3、使用DatagramSocket类的receive()方法接收UDP

获取的数据实际上就存储在创建空包的数组种,转换显示时建议设置长度
System.out.println(dp.getLength()); //数据的具体长度
String str=new String(buffer,0,dp.getLength());

一般来说UDP协议的最大数据包的长度64k

编码实现

发送方

package com.test;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class Send {
	public static void main(String[] args) throws Exception {
		DatagramSocket ds = new DatagramSocket();
		String msg = "now?";
		byte[] buffer = msg.getBytes();
		DatagramPacket dp = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("localhost"), 8888);
		ds.send(dp); // 发送信息

		buffer = new byte[8192];
		dp = new DatagramPacket(buffer, buffer.length);
		ds.receive(dp); // 接受信息
		msg = new String(dp.getData(), 0, dp.getLength());
		System.out.println("Send:" + msg);
		ds.close();
	}
}

接收方

package com.test;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Rec {
	public static void main(String[] args) throws Exception {
		DatagramSocket ds = new DatagramSocket(8888);
		byte[] buffer = new byte[8192];
		DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
		ds.receive(dp); // 接收信息
		String ss = new String(buffer, 0, dp.getLength());
		if ("now?".equals(ss)) {
			Date now = new Date();
			DateFormat df = new SimpleDateFormat("yyyy-MM-ddE hh:mm:ss");
			ss = df.format(now);
			InetAddress ia = dp.getAddress();
			int port = dp.getPort();
			buffer = ss.getBytes();
			dp = new DatagramPacket(buffer, buffer.length, ia, port);
			ds.send(dp); // 发送信息
		}
		ds.close();
	}
}

多播或者组播的实现
真正聊天室的实现原理

MulticastSocket

MulticastSocket可以将数据报以广播的方式发送给加入指定组的所有客户端

组播是指把信息同时传递给一组目的地址。它使用的策略是最高效的,因为消息在每条网络链路上只需传递一次,且只有在链路分叉的时候,消息才会被复制。与多播相比,常规的点到单点传递被称作单播。当以单播的形式把消息传递给多个接收方时,必须向每个接收者都发送一份数据副本。由此产生的多余副本将导致发送方效率低下,且缺乏可扩展性。不过,许多流行的协议——例如XMPP,用限制接收者数量的方法弥补了这一不足

5. 代理服务器

JDK1.5提供了Proxy和ProxySelector类来实现代理访问。

Proxy代表一个代理服务器,可以在打开URLConnection连接时指定代理,也可以在创建Socket连接时指定代理。ProxySelector是一个代理选择器,提供了对代理服务器更加灵活的控制,可以对http\https\ftp\socket等进行分别设置,还可以设置不需要通过代理服务器的主机和地址

代理服务器的功能

  • 突破自身IP限制
  • 提高访问速度

大作业 网盘

要求:实现一个网盘管理功能

上传、下载、删除、列表显示所有目录和文件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值