网络编程

1 网络编程的基础知识

1.网络模型 5层模型和七层模型
2.网络编程的三要素 ip 端口 协议
3.Tcp连接过程 三次握手和四次挥手

1 网络模型

https://blog.csdn.net/qq_22238021/article/details/80279001
在这里插入图片描述
在这里插入图片描述
Tcp连接过程 三次握手和四次挥手
https://baijiahao.baidu.com/s?id=1596016296668694374&wfr=spider&for=pc
在这里插入图片描述
在这里插入图片描述

2 网络编程的三要素

1.IP

2.端口
3.协议

eg: 找个泰国人说话
ip类似于一个人的身份证
端口类似于门牌号
协议类似两个人说话的语言

3 IP地址:

网络中针对计算机的唯一表示,类似于一个人的身份证,每个计算机都有自己的ip地址

计算机中是用点分十进制来表示的: 192.168.5.76
ip的存储是使用二进制存储
11000000.10101000.00000101.01001100

分为网络区段号和主机段号
IP地址分类

A类地址: 第一段为网络区段号,后面三段为主机段号 256 * 256 * 256 = 16777216 国家 政府 军方
B类地址: 前两段为网络区段号,后面两段为主机段号 256 * 256 = 65536 学校 公司
C类地址: 前三段为网络区段号,后面一段为主机段号 256 尚学堂 网吧
特殊地址:
127.0.0.1 : 本机地址/回环地址 == localhost == 192.168.5.76
192.168.x.x : 表示私有地址 (这个地址不能够在互联网上使用,只能够在局域网中使用)
x.x.x.25: 表示广播地址

掌握2个命令:

ipconfig: 查看本机网络配置
ping ip地址: 测试是否能够和目标ip进行正常的发送和接收数据包

4 端口: 同一台计算机进程的标识

端口的范围: 0~65535是我们可以使用端口的范围
0~1024一般是系统进程端口,不建议使用
1024~65535可以是我们使用的端口
建议我们不要和系统进程或者和已经打开的程序端口重复,否则会出现端口被占用的问题

5 协议: 不同计算机通信的规则

HTTP: 超文本传输协议
FTP: 文件传输协议
TCP: 传输控制协议
UDP: 用户数据报协议

TCP协议的特点:
1.面向连接的协议
2.数据传输量没有限制
3.数据安全,可靠
4.速度慢
eg: 文件上传 文件下载

UDP协议:
1.面向无连接
2.数据传输量有限制,最好不要超过64K
3.速度快
4.数据不安全,很容丢失数据
5.传输数据之前必须要打包处理
eg: 发短信 群聊

Java是面向对象语言: Ip提供了对应的Java类 InetAddress

public class NetDemo01 {
	public static void main(String[] args) throws UnknownHostException {
		// 创建IP对象
		// static InetAddress getByName(String host) 
//		InetAddress ip = InetAddress.getByAddress(new byte[] {(byte) 192,(byte) 168,5,76} );
		InetAddress ip = InetAddress.getByName("192.168.5.76");
//		InetAddress ip = InetAddress.getByName("www.baidu.com");
		System.out.println(ip);
		String hostAddress = ip.getHostAddress();
		System.out.println(hostAddress);
		String hostName = ip.getHostName();
		System.out.println(hostName);
		InetAddress loopbackAddress = InetAddress.getLoopbackAddress();
		System.out.println(loopbackAddress);
	}
}

2 UDP协议

Socket成为套接字
网络编程的本质就是 Socket编程 (IO流 + 多线程 + Socket)
服务器端和客户端都会有一个对应的Socket对象进行数据传输和交互

UDP协议特点:

1.面向无连接
2.数据传输量有限制,最好不要超过64K
3.速度快
4.数据不安全,很容丢失数据
5.传输数据之前必须要打包处理
eg: 发短信 群聊

UDP协议的原理就类似于寄快递

1 UDP协议实现数据的发送和接收

UDP编程发送端 如下代码

public class ClientDemo {
	public static void main(String[] args) throws IOException {
		System.out.println("发送端启动了...");
		// 创建IP对象
		InetAddress clientIp = InetAddress.getByName("192.168.5.76");
		int clientPort = 10086;
		// 1.创建Socket对象  DataGramSocket
		// 这里的ip和端口指的是发送端的ip和端口
		DatagramSocket ds = new DatagramSocket(clientPort, clientIp);
		// 2.创建数据
		String data = "HelloWorld";
		// 3.对数据进行打包
		byte[] buf = data.getBytes();
		int length = buf.length;
		InetAddress serverIp = InetAddress.getByName("192.168.5.76");
		int serverPort = 12306;
		DatagramPacket dp = new DatagramPacket(buf, length, serverIp, serverPort);
		// 4.调用socket的send方法发送数据包
		ds.send(dp);
		// 5.释放资源
		ds.close();
	}
}

UDP编程接受端 如下代码

public class ServerDemo {
	public static void main(String[] args) throws Exception {
		System.out.println("接收端启动了...");
		// 1.创建接收端Socket对象
		DatagramSocket ds = new DatagramSocket(12306, InetAddress.getByName("192.168.5.76"));
		// 2.创建一个空的包裹用于存储拆包的数据
		byte[] buf = new byte[100];
		DatagramPacket dp = new DatagramPacket(buf, buf.length);
		// 3.调用Socket对象的receive方法来接受传递过来的数据包到我们创建好的空包中
		// 这还是一个阻塞方法,会阻塞当前正在执行的线程,等待包裹传递过来,一旦接受到包裹,程序继续执行
		ds.receive(dp); 
		// 4.解析包裹
		byte[] data = dp.getData();
		String clientIp = dp.getAddress().getHostAddress();
		int clientPort = dp.getPort();
		int length = dp.getLength();
		
		String result = new String(data, 0, length);
		System.out.println("来自于" + clientIp + ",端口号为: " + clientPort + "的数据:" + result);
		
		// 5.释放资源
		ds.close();
	}
}

2 UDP协议实现群聊功能

发送端

/*
 * 网络编程的本质就是 Socket编程 (IO流 + 多线程 + Socket)
 * Socket成为套接字
 * 服务器端和客户端都会有一个对应的Socket对象进行数据传输和交互
 * 
 * UDP协议的原理就类似于寄快递
 */
public class ClientDemo {
	public static void main(String[] args) throws IOException {
		System.out.println("老肖聊天室启动了...");
		
		Scanner input = new Scanner(System.in);
		DatagramSocket ds = new DatagramSocket(12306);
		
		while (true) {
			System.out.print("请输入:");
			String line = input.nextLine();
			
			if (line.equals("byebye")) {
				break;
			}
			
			byte[] buf = line.getBytes();
			DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.5.76"), 9999);
			
			ds.send(dp);
		}
		
		ds.close();
		input.close();
	}
}

接收端

public class ServerDemo {
	public static void main(String[] args) throws Exception {
		System.out.println("聊天服务器启动了...");
		// 默认使用本机作为服务器
		DatagramSocket ds = new DatagramSocket(9999);
		
		while (true) {
			byte[] buf = new byte[1024];
			DatagramPacket dp = new DatagramPacket(buf, buf.length);
			
			ds.receive(dp);
			
			byte[] data = dp.getData();
			int length = dp.getLength();
			
			String ip = dp.getAddress().getHostAddress();
			String name = "";
			Properties prop = PropertiesUtil.prop;
			Set<String> keys = prop.stringPropertyNames();
			for (String ipKey : keys) {
				if (ipKey.equals(ip)) {
					name = prop.getProperty(ipKey);
				}
			}
			
			String result = new String(data, 0, length);
			
			if (ip.equals("192.168.5.76")) {
				System.err.println(name + "说:" + result);
			} else {
				System.out.println(name + "说:" + result);
			}
		}
	}
}

3 tcp协议

tcp协议特点

1.面向连接的协议,必须先建立连接才能通信,三次握手建立连接
2.数据传输量没有限制
3.数据不会丢失,安全可靠
4.效率低

1 TCP协议发送数据和接收数据

TCP协议发送数据

 public class ClientDemo {
	public static void main(String[] args) throws IOException {
		System.out.println("客户端启动了...");
		// 1.创建Socket对象
		Socket s = new Socket("192.168.5.76", 10086);
		// 2.通过Socket对象和服务器对应的Socket对象进行通信
		OutputStream os = s.getOutputStream();
		// 3.通过输出流输出数据
		String data = "HelloWorld";
		os.write(data.getBytes());
		// 4.释放资源
		s.close();
	}
}

tcp协议接收数据

public class ServerDemo {
	public static void main(String[] args) throws Exception {
		System.out.println("服务器启动了....");

		// 1.创建服务器端Socket
		ServerSocket ss = new ServerSocket(10086);
		// 2.监听客户端的连接
		// 该方法是一个阻塞方法,监听客户端的连接,一旦连接成功会返回与客户端匹配的Socket对象
		Socket s = ss.accept();
		// 3.通过返回的对应客户端的Socket对象来获取输入流
		InputStream is = s.getInputStream();
		// 4.通过io流读取数据
		byte[] bys = new byte[100];
		int len = is.read(bys);
		String data = new String(bys, 0, len);
		// 5.显示数据
		System.out.println("客户端传递过来的消息是: " + data);
		// 6.释放资源
		s.close();
		ss.close();
	}
}

2 tcp协议实现文件上传

发送端

/*
 * 将文件读取出来写入到服务器,同时接收服务器的响应字符串
 */
public class FileUploadClient {
	public static void main(String[] args) throws Exception {
		Socket s = new Socket("192.168.5.76", 10086);
		System.out.println("客户端连接成功!!!");
		
		BufferedReader br = new BufferedReader(new FileReader("client/NetDemo01.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();
		System.out.println("==============>>接收服务器响应");
		
		InputStream is = s.getInputStream();
		byte[] bys = new byte[100];
		int len = is.read(bys);
		System.out.println("服务器响应:" + new String(bys, 0, len));
		
		br.close();
		s.close();
	}
}

接收端

/*
 * 文件上传服务器
 * tomcat
 * MIME类型
 */
public class FileUploadServer {
	public static void main(String[] args) throws IOException {
		
		ServerSocket ss = new ServerSocket(10086);
		System.out.println("文件服务器启动了...");
		
		// 这种写法存在问题: 第一个文件没有上传完毕,那么后面的文件都不能够上传,不能够同时上传
		while (true) {
			Socket s = ss.accept();
			
			BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
			BufferedWriter bw = new BufferedWriter(new FileWriter("server/server.txt"));
			
			String line = null;
			while ((line = br.readLine()) != null) {
				bw.write(line);
				bw.newLine();
				bw.flush();
			}
			
			// 关闭输入流 (半关闭的Socket)
			s.shutdownInput();
			
			System.out.println("==============>>给出客户端响应");
			/*
			 * 数据源: 内存中的 文件上传成功!!!
			 * 目的地: 网络流中
			 * 交通工具: 字节流
			 */
			OutputStream os = s.getOutputStream();
			os.write("文件上传成功".getBytes());
			
			os.close();
			bw.close();
		}
		
	}
}

3 多线程+TCP实现多个视频同时上传

发送端

*
 * 文件上传客户端
 */
public class FileUploadClient1 {
	public static void main(String[] args) throws Exception {
		Socket s = new Socket("192.168.5.76", 10086);
		
		OutputStream os = s.getOutputStream();
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("video/01.进程和线程.mp4"));
		BufferedOutputStream bos = new BufferedOutputStream(os);
		
		int len = 0;
		byte[] bys = new byte[1024];
		
		while ((len = bis.read(bys)) != -1) {
			bos.write(bys, 0, len);
			bos.flush();
		}
		
		s.shutdownOutput();
		// 读取服务器响应
		InputStream is = s.getInputStream();
		len = is.read(bys);
		System.out.println(new String(bys, 0, len));
		
		bis.close();
		s.close();
	}
}

接收端

/*
 * 文件上传客户端
 */
public class FileUploadClient2 {
	public static void main(String[] args) throws Exception {
		Socket s = new Socket("192.168.5.76", 10086);
		
		OutputStream os = s.getOutputStream();
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("video/02.线程的开启方式一_继承Thread方式开启线程.mp4"));
		BufferedOutputStream bos = new BufferedOutputStream(os);
		
		int len = 0;
		byte[] bys = new byte[1024];
		
		while ((len = bis.read(bys)) != -1) {
			bos.write(bys, 0, len);
			bos.flush();
		}
		
		s.shutdownOutput();
		// 读取服务器响应
		InputStream is = s.getInputStream();
		len = is.read(bys);
		System.out.println(new String(bys, 0, len));
		
		bis.close();
		s.close();
	}
}

4 tcp协议在网络中传输对象

将学生类写入到网络中

public class ClientDemo {
	public static void main(String[] args) throws Exception {
		Socket s = new Socket("192.168.1.112", 7878);
		
		OutputStream os = s.getOutputStream();
		
		ObjectOutputStream oos = new ObjectOutputStream(os);
		
		oos.writeObject(new Student("隔壁老王", 30));
		
		ServerSocket ss = new ServerSocket(7878);
		
		Socket s2 = ss.accept();
		
		InputStream is = s2.getInputStream();
		
		ObjectInputStream ois = new ObjectInputStream(is);
		
		Object obj = ois.readObject();
		
		if (obj instanceof Student) {
			Student stu = (Student) obj;
			System.out.println(stu.getName() + ":" + stu.getAge());
		}
		
		ss.close();
	}
}

//注意要实现Serializabl
public class Student implements Serializable {
	private static final long serialVersionUID = -6552531012158918514L;
	private String name;
	private int age;
	
	public Student() {}

	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}
	
}

5 通过键盘输入写入到文件

发送端

// 客户端通过键盘输入,服务器端写入到文件
public class ClientDemo {
	public static void main(String[] args) throws Exception {
		// 客户端通过键盘输入
		Socket s = new Socket(Global.IP_SERVER, Global.PORT_SERVER);
		System.out.println("客户端成功连接上了服务器...");
		
		/*
		 * 数据源: 键盘输入
		 * 目的地: 网络中
		 * 交通工具: 
		 * 		System.in
		 * 		s.getOutputStream
		 */
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
		
		String line = null;
		while ((line = br.readLine()) != null) {
			bw.write(line);
			bw.newLine();
			bw.flush();
		}
		
		s.close();
	}
}

接收端

// 客户端通过键盘输入,服务器端写入到文件
public class ServerDemo {
	public static void main(String[] args) throws Exception {
		// 服务器端写入到文件
		ServerSocket ss = new ServerSocket(Global.PORT_SERVER);
		System.out.println("服务器端启动成功了...");
		
		Socket s = ss.accept();
		
		InputStream is = s.getInputStream();
		/*
		 * 数据源: 网络中
		 * 目的地: 文件中
		 * 交通工具: 
		 * 		s.getInputStream()
		 * 		BufferedWriter
		 */
		BufferedReader br = new BufferedReader(new InputStreamReader(is));
		BufferedWriter bw = new BufferedWriter(new FileWriter("server/server.txt"));
		
		String line = null;
		while ((line = br.readLine()) != null) {
			bw.write(line);
			bw.newLine();
			bw.flush();
		}
		
		br.close();
		bw.close();
		s.close();
		ss.close();
	}
}

6 properties配置文件中产生中文乱码的问题

public class PropertiesUtil {
    public static String default_properties = "config/ipconfig.properties";
    public static Properties prop;
    static {
        prop = new Properties();
        try {
            InputStream is = new BufferedInputStream(new FileInputStream(default_properties));
            //解决读取properties文件中产生中文乱码的问题
            BufferedReader bf = new BufferedReader(new InputStreamReader(is,"UTF-8"));
            prop.load(bf);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    public static String getProperty(String key) {
        return prop.getProperty(key);
    }
 
    public static String getProperty(String key, String defaultValue) {
        String value = prop.getProperty(key);
        if (value == null)
            return defaultValue;
        return value;
    }
 
    public static boolean getBooleanProperty(String name, boolean defaultValue) {
        String value = prop.getProperty(name);
        if (value == null)
            return defaultValue;
        return (new Boolean(value)).booleanValue();
    }
 
    public static int getIntProperty(String name) {
        return getIntProperty(name, 0);
    }
 
    public static int getIntProperty(String name, int defaultValue) {
        String value = prop.getProperty(name);
        if (value == null)
            return defaultValue;
        return (new Integer(value)).intValue();
    }
 
    public static void main(String[] args) {
        Set<String> keys = PropertiesUtil.prop.stringPropertyNames();
        for (String key : keys) {
        	String value = prop.getProperty(key);
        	System.out.println(key + "=" + value);
		}
    }
}

4 http协议

该类用于处理浏览器发送过来的http请求

请求协议的格式: http://ip:端口/路径1/路径2/…/路径n/web资源 (html css js xml json png jpg)
例如:http://localhost/admin/admin.html
浏览器默认端口是80端口,如果浏览器地址没有写明端口,默认请求的就是服务器的80端口

HTTP协议的格式

请求协议
请求首行 GET /helloworld/index.jsp HTTP/1.1
请求头信息
空行
请求体

例如:

GET /helloworld/index.jsp HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8 MIME类型

get没有请求体

响应协议
响应首行 HTTP/1.1 200 OK
响应头信息
空行
响应体
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=UTF-8
Content-Length: 367
Set-Cookie: JSESSIONID=C97E2B4C55553EAB46079A4F263437B6; Path=/helloworld
Date: Wed, 16 Sep 2017 03:15:43 GMT

该线程的任务:
1.解析请求参数
2.将浏览器请求的web资源返回给浏览器

public class HandlerRequestThread implements Runnable {

	private Socket s;

	public HandlerRequestThread() {
		super();
	}

	public HandlerRequestThread(Socket s) {
		super();
		this.s = s;
	}

	@Override
	public void run() {
		PrintWriter pw = null;
		OutputStream os = null;
		String webServerRoot = "";

		try {
			InputStream is = s.getInputStream();
			BufferedReader br = new BufferedReader(new InputStreamReader(is));
			StringBuilder sb = new StringBuilder();

			String line = null;
			while ((line = br.readLine()) != null && line.length() > 0) {
				sb.append(line);
				sb.append("\r\n");
			}

			System.out.println(sb.toString());
			String[] messages = sb.toString().split(" ");

			// 给出浏览器响应
			webServerRoot = "D:/WebRoot";

			os = s.getOutputStream();

			pw = new PrintWriter(new OutputStreamWriter(os));
			// 给浏览器写响应首行
			pw.println("HTTP/1.1 200 OK");
			pw.println("Content-Type: */*;charset=UTF-8");
			// 给浏览器写空行
			pw.println();
			// 给浏览器写响应体
			pw.flush();

			if (messages.length > 2) {
				File f = new File(webServerRoot + messages[1]);
				FileInputStream fis = new FileInputStream(f);

				byte[] bys = new byte[1024];
				int len = 0;
				while ((len = fis.read(bys)) != -1) {
					os.write(bys, 0, len);
					os.flush();
				}

				fis.close();
			}

		} catch (FileNotFoundException e) {
			File errorFile = new File(webServerRoot, "error.html");
			
			try {
				FileInputStream fis = new FileInputStream(errorFile);

				byte[] bys = new byte[1024];
				int len = 0;
				
				while ((len = fis.read(bys)) != -1) {
					os.write(bys, 0, len);
					os.flush();
				}
				
				fis.close();
			} catch (Exception e2) {
				e2.printStackTrace();
			}
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值