黑马程序员——网络编程——服务器和URL、URLConnection对象

本文深入探讨了Java中的网络编程,包括自定义服务端的实现,了解客户端向服务端发送请求的过程,并模拟浏览器获取信息。进一步讲解了URL和URLConnection的概念及其在网络结构中的应用,区分了URI与URL的差异,并给出了相关示例。同时,文章讨论了C/S和B/S架构的特点和优缺点。最后,提供了TCP协议上传图片的客户端和服务端练习。

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


网络编程

客户端服务端的原理:

生活中:

常见的客户端:浏览器:IE

常见的服务端:服务器:tomcat

一、自定义服务端:

客户端使用电脑上的浏览器即可;

1.      了解一下客户端向服务端发送的请求

import java.io.*;
import java.net.*;
class MyServer 
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss = new ServerSocket(6060);
		Socket s = ss.accept();
		System.out.println(s.getInetAddress().getHostAddress()+"已连接");

		InputStream is = s.getInputStream();

		byte[] buf = new byte[1024];

		int len = is.read(buf);

		String text = new String(buf,0,len);

		System.out.println(text);
		
		//反馈信息
		PrintWriter pW = new PrintWriter(s.getOutputStream(),true);

		pW.println("欢迎光临");

		s.close();
		ss.close();

	}
}

result:


发送的数据(请求)为:
127.0.0.1已连接
GET / HTTP/1.1
Host: 127.0.0.1:6060
Connection: keep-alive
Cache-Control: max-age=0
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like
Gecko) Chrome/31.0.1650.63 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8

2.      模拟一个浏览器获取信息

import java.io.*;
import java.net.*;

class MyBrowser 
{
	public static void main(String[] args) throws Exception
	{
		
		Socket s = new Socket("127.0.0.1",80);
		//模拟浏览器向Tomcat服务器反射http协议的请求消息
		PrintWriter pW = new PrintWriter(s.getOutputStream(),true);
		pW.println("GET /MyBrowser.html HTTP/1.1");
		
		pW.println("Accept:*/*");
		pW.println("Host:127.0.0.1:8080");
		pW.println("Connection:close");
		pW.println("");

		InputStream is = s.getInputStream();
		byte[] buf = new byte[1024];
		int len = is.read(buf);
		String text = new String(buf,0,len);
		System.out.println(text);
		s.close();
	}
}

result:


二、URL&URLConnection

URI:统一资源标示符

         URL:统一资源定位符,也就是说根据URL能够定位到网络上上的某个资源,它是指向互联网资源的指针。

         每个URL都是URI,但是不一定每个URI都是URL。URI还包括URN(统一资源名称),它命名资源但不指定如何定位资源。

示例:

import java.io.*;
import java.net.*;

class URLDemo 
{
	public static void main(String[] args) throws Exception
	{
		String str = "http://localhost/MyBrowser.html";

		URL url = new URL(str);
		sop("getProtocol:"+url.getProtocol());
		sop("getHost:"+url.getHost());
		sop("getPort:"+url.getPort());
		sop("getFile:"+url.getFile());
		sop("getPath:"+url.getPath());
		sop("getQuery:"+url.getQuery());
		
		InputStream is = url.openStream();//相当于url.openConnection().getInputStream();

		byte[] buf = new byte[1024];
		int len = is.read(buf);

		String text = new String(buf,0,len);

		sop(text);

		is.close();
	}
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

result:


示例:URlConnection

import java.net.*;

class  URLDemo1
{
	public static void main(String[] args) throws Exception
	{
		String str = "http://localhost/MyBrowser.html";

		URL url = new URL(str);

		//获取url对象的Url连接器对象。将连接封装成了对象:java中内置的可以解析的具体协议对象+socket
		URLConnection conn = url.openConnection();

		System.out.println(conn);
		System.out.println("----------------------");

		//由于URLConnection对象已经把相应头给解析了,所以可以通过URLconnection对象获取响应头某属性名对应的属性值
		String value = conn.getHeaderField("Content-Type");
		System.out.println(value);
	}
}

result:


常见网络结构:

1.      C/S client/server

特点:客户端,服务端都要开发  开发成本高,维护麻烦

好处:客户端可以在本地分担一些任务,减轻服务端的压力

2.      B/s browser/server

特点:只要开发服务端,客户端用浏览器代替,开发成本较低,维护简单

缺点:所有运算在服务端,服务端压力大

练习:TCP协议上传图片客户端和服务端

import java.io.*;
import java.net.*;

class  TCPServerDemo
{
	public static void main(String[] args) throws Exception
	{
		System.out.println("-------1------");
		ServerSocket ss = new ServerSocket (10001);
		Socket s = ss.accept();
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+"------已连接");

		InputStream is = s.getInputStream();

		File filePath = new File(ip+".jpg");
		FileOutputStream fos = new FileOutputStream(filePath);
		byte[] buf = new byte[1024];
		int len = 0;
		while((len = is.read(buf))!=-1){
			fos.write(buf,0,len);
		}
		System.out.println("-------2------");
		OutputStream out = s.getOutputStream();
		out.write("上传成功".getBytes());

		fos.close();
		s.close();
		ss.close();
	}
}
class TCPClientDemo
{
	public static void main(String[] args)throws Exception{
		Socket s = new Socket("127.0.0.1",10001);
		FileInputStream fis = new FileInputStream("1.jpg");
		OutputStream out = s.getOutputStream();
		System.out.println("-------1------");
		byte[] buf = new byte[1024];
		int len = 0;

		while((len = fis.read(buf))!=-1){
			out.write(buf,0,len);
		}
		System.out.println("-------2------");
		s.shutdownOutput();
		System.out.println("-------3------");
		InputStream is = s.getInputStream();
		
		len = is.read(buf);
		String text = new String(buf,0,len);
		System.out.println(text);
		fis.close();
		s.close();
	}
}

result:

客户端


服务端


拓展:

/*
思想:服务器CPU分配给每条线程的时间片是相同的,即服务器的带宽平均分配给每条线程,所以客户端开启的线程越多,就能抢到越多的服务器资源。

*/
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class MulThreadDown {

	static int ThreadCount = 3;//能开启线程数为3个
	static int finishedThread = 0;
	static String path = "http://............";//这里写上下载的路径地址
	public static void main(String[] args) {
		
		//发送get请求,请求这个地址的资源
		try {
			String fileName = "lol游戏压缩包.zip"
			//传入地址,创建URL对象
			URL url = new URL(path);
			//开启网络连接
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			//设置get请求
			conn.setRequestMethod("GET");
			//设置连接延迟5s和读取文件延迟5s
			conn.setConnectTimeout(5000);
			conn.setReadTimeout(5000);
			//如果返回码为200,说明连接建立
			if(conn.getResponseCode() == 200){
				//拿到所请求资源文件的长度
				int length = conn.getContentLength();
				File file = new File(fileName);
				//生成临时文件,即要下载的资源要存储到这个文件中去
				RandomAccessFile raf = new RandomAccessFile(file, "rwd");
				//设置临时文件的大小
				raf.setLength(length);
				raf.close();
				//计算出每个线程应该下载多少字节
				//每个线程应该是平均分要下载的资源大小,注意:最后一个线程要下载剩余全部的文件
				int size = length / ThreadCount;
				
				for (int i = 0; i < ThreadCount; i++) {//i代表开启了第几个线程
					//计算线程下载的开始位置和结束位置
					int startIndex = i * size;
					int endIndex = (i + 1) * size - 1;
					//如果是最后一个线程,下载剩下的全部文件
					if(i == ThreadCount - 1){
						endIndex = length - 1;
					}
					new DownLoadThread(startIndex, endIndex, i).start();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
}
class DownLoadThread extends Thread{
	int startIndex;//每个线程开始下载文件的位置
	int endIndex;//每个线程下载的文件的结束位置
	int threadId;//线程ID
	
	public DownLoadThread(int startIndex, int endIndex, int threadId) {
		super();
		this.startIndex = startIndex;
		this.endIndex = endIndex;
		this.threadId = threadId;
	}

	@Override
	public void run() {
		//再次发送http请求,下载原文件
		try {
			File progressFile = new File(threadId + ".txt");
			//判断进度临时文件是否存在
			if(progressFile.exists()){
				FileInputStream fis = new FileInputStream(progressFile);
				BufferedReader br = new BufferedReader(new InputStreamReader(fis));
				//从进度临时文件中读取出上一次下载的总进度,然后与原本的开始位置相加,得到新的开始位置
				startIndex += Integer.parseInt(br.readLine());
				fis.close();
			}
			HttpURLConnection conn;
			URL url = new URL(MulThreadDown.path);
			conn = (HttpURLConnection) url.openConnection();
			conn.setRequestMethod("GET");
			conn.setConnectTimeout(5000);
			conn.setReadTimeout(5000);
			//通过HTTP请求头中的“Range”这个字段,设置本次http请求所请求的数据的区间
			conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
			//请求部分数据,相应码是206,如果返回了206,说明下载连接成功
			if(conn.getResponseCode() == 206){
				//流里此时只有1/3原文件的数据
				InputStream is = conn.getInputStream();
				byte[] b = new byte[1024];
				int len = 0;
				int total = 0;
				//拿到临时文件的输出流
				File file = new File(fileName);
				RandomAccessFile raf = new RandomAccessFile(file, "rwd");
				//把文件的写入位置移动至startIndex,断点续传
				raf.seek(startIndex);
				while((len = is.read(b)) != -1){
					//每次读取流里数据之后,同步把数据写入临时文件
					raf.write(b, 0, len);
					total += len;
					//生成一个专门用来记录下载进度的临时文件
					RandomAccessFile progressRaf = new RandomAccessFile(progressFile, "rwd");
					//每次读取流里数据之后,同步把当前线程下载的总进度写入进度临时文件中
					progressRaf.write((total + "").getBytes());
					progressRaf.close();
				}
				raf.close();
				MulThreadDown.finishedThread++;
				synchronized (MulThreadDown.path) {
					if(MulThreadDown.finishedThread == MulThreadDown.ThreadCount){
						//如果下载完成线程数为设置的结束线程数,则下载完成,删除临时文件
						for (int i = 0; i < MulThreadDown.ThreadCount; i++) {
							File f = new File(i + ".txt");
							f.delete();
						}
						MulThreadDown.finishedThread = 0;
					}
				}
				
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}






  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值