黑马程序员————Java基础日常笔记---网络编程

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

黑马程序员————Java基础日常笔记---网络编程

1.1网络编程由来:

以前是单机版的,现在是联机的,
唯一不同的涉及到了网络,

例如:

要和张三通讯, 需要先找到张三的那台主机,IP地址有四段,目的:为了不重复,
从我这里QQ发信息,先发到对方的IP中, 然后在传输到对方的QQ中,

如何做?

1, 找到对方IP

2, 数据要发送到对方指定的应用程序中,为了标识这些应用程序, 所以需要给这些网络应用程序都用数字进行标识;

用MSN发到对方IP地址的对应数字标识2900的应用程序上,为了称呼这个数字,叫做端口(逻辑端口)。

而一部分人会认为端口就是网线接口(物理端口)

MSN和QQ和我聊天,但是为什么用MSN聊的,不发到QQ上来呢?

走的是MSN所对应的数字标识, 所以不会走错, 形象的称之为端口(逻辑端口)

如果用MSN,发到QQ上, 是解析不了,因为不是对应的数字标识(端口)。

3,两个需要进行通信, 需要有规则, 称为协议各国都可以进行通信了, 这个原因是都遵从了一个数据规则)

国际组织定义的通用的协议: TCP/IP;

一般的协议有两种:

UDP,TCP

解释:

IP地址: 先找大厦地址,就是这个IP地址

端口:大厦中有很多屋子, 这是就是找的是端口, 406这个房间,可以接待你

如图:

1.2 网络模型

问题:两台计算机是如何通信的呢?
那么就有了网络的模型的概念。

如图:

 

-->1先使用应用层的特点, 将数据封装, 应用层也有自己的数据表现(封装)规则,
每个层次对数据进行数据的封装;
-->2, 传输层有 UDP TCP , 到了这一层就把数据该有的信息打包完了,
打包发到了第四个,
-->3,网络层就是给数据一个 IP 地址, IP地址协议就在网络层,
-->4, 随后到了物理层,  网线 就是标准的物理层设备,
这时这个数据被封装成了数据包了,
以上就是数据封包的过程;
按照每一层的不同加上了每一层的特有信息,
这时通过网线(光纤,无线)传出去了;
-->到了另一端主机就是数据拆包了,
拆完之后这个数据给谁呢? 看这个数据到底要走哪个端口?
如果给这个数据的端口是4000的话, 那么端口是4000的这个应用程序就开始解析这个数据了,
以上就是网络底层传输数据的基本原理
如图所示:
 

1.3 IP地址
用java语言来操作这三个要素,所以学习这个对象, 所以有一个专门用于描述ip对象的这么一个类,

代码如下:

<span style="font-size:12px;">/*
static InetAddress getLocalHost() 
         返回本地主机。
String getHostAddress() 返回 IP 地址字符串
String getHostName() 获取此 IP 地址的主机名

static InetAddress  getByName(String host)  
		在给定主机名的情况下确定主机的 IP 地址。
          byte[]	getAddress() 
        返回此 InetAddress 对象的原始 IP 地址。
*/
import java.net.*;
class IpDemo
{
	public static void main(String[] args) throws Exception
	{
		InetAddress i=InetAddress.getLocalHost();//返回本地主机。表示互联网协议 (IP) 地址。 
		System.out.println("address::"+i.getHostAddress()+"...name::"+i.getHostName());

		//获取任意一台主机的IP地址对象
		//InetAddress i1=InetAddress.getByName("192.168.1.102");
		//System.out.println("address::"+i1.getHostAddress()+"...name::"+i1.getHostName());
	}
}</span><span style="font-size: 14px;">
</span>

1.4 传输协议

UDP和TCP
他其实就是一个通讯的规则(步话机和电话的区别)
UDP协议:
1,将数据,源和目的封装成数据报包中,不需要建立连接
2,每个数据报包的大小限制在64k以内
3,因为无连接,所以他是不可靠的协议
4,不需要建立连接,因此速度快
生活中:
       聊天就是UDP, 在不在不重要,求速度,
      桌面共享,网络视频会议,UDP;

TCP协议:
1,建立连接,形成传输数据的通道
2,在连接中进行大数据量传输
3,通过三次握手完成连接,是可靠协议
4,必须建立连接,效率会稍低
自己对三次握手的个人理解:
TCP就像是双方在打电话, 
首先:A打电话给B, 
A拨号, 发出连接请求,拨通了,就
说:喂!你好,
B说:你好,在的,
A说:好,然后就说。。。。。。
互相确认后,完成TCP传输的建立;

UDP就相当于步话机 ,一方在说, 另一方在不在不所谓。

1.5 Socket
网络编程具体实现: 其实就是Socket服务,
Socket就Socket是为网络服务提供了一种机制,或者说服务更为确切。
就像邮局,港口,码头一样,

因此需要数据传输,那么需要有两个站点提供服务,一个是发,一个是收;
所以通信的两端都需要有Socket, 
因此网络通信其实就是Socket间通信;
而对网络通信其实也是对数据的操作,因此Socket间需要通过IO流传输。


由于传输协议不同, 每个传输协议都有自己不同的建立端点的方式,

像用民用船传输货物, 还是军用船传输货物,不一样的,

于是服务就有了两种方式传输数据, UDP和TCP传输了。


那么UDP的Socket服务该如何去建立呢?

1,建立发送端,接收端(涉及了两个对象(DatagramSocket与DatagramPacket))

2,建立数据包

3,调用Socket的发送接收方法

4,关闭Socket

发送端与接收端是两个独立的运行程序。

套接字:也就是插座,码头,

而接收和发送都是需要参数的,数据包, 因此需要指定数据包的对象,为什么是数据包的对象呢?

需要地址和端口, 源和目的的地址和端口,都需要封装到数据包中,

 因此数据包是比较复杂的事物,所以需要将数据包封装成一个对象


而在这个对象的构造函数中, 就需要明确数据,接收端Ip地址,端口

因此如果通过upd传输的方式,将一段文字数据发送出去,定义一个udp发送端。

所以UDP发送端:

1,建立UDPsocket服务

2,提供数据,并将数据封装到数据包中。

  3,通过socket服务的发送功能,将数据包发出去

4,关闭资源

代码如下: 

import java.net.*;

class UdpSend
{
	public static void main(String[] args) throws Exception
	{
		//创建udp服务,建立发送数据的端点,通过DatagramSocket对象
		DatagramSocket ds=new DatagramSocket();

		//确定数据,并封装成数据包,
		//DatagramPacket(byte[] buf, int length, InetAddress address, int port) 
        //构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
		byte[] buf="udp is coming".getBytes();
		//DatagramPacket dp=new DatagramPacket(buf,buf.length,InetAddress.getLocalHost.getHostAddress(),10000);
		DatagramPacket dp=new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.102"),10000);
		
		//通过socket服务,将已有的数据包发送出去,通过send方法
		ds.send(dp);

		//关闭资源
		ds.close();
	}
}
结果

通过结果看到,没有任何东西, 发到哪里不知道,可能丢失了, 这就是面向无连接的特点(步话机--一一端在说话,另一端在不在不知道)。

接收端没有开, 那如何让这个数据收到呢?

 UDP接收端

通讯需要有两个端点,一般是将文件封装到两个文件夹中,

发送和接收端是两个独立的应用程序;

因此需要定义一个应用程序, 用于接收UDP协议传输的数据并处理。

步骤如下: 

1, 定义UDPsocket服务

2, 接收到数据后, 需要把数据的具体信息提取出来,

因为发过来的数据包含源和目的的地址和端口,还有数据内容,这么多数据内容如果要提取出来,比较痛苦, 所以把接收到的字节全都存到数据包中,因为数据包中有相对应的功能可以提取数据的不同信息,封装对象的好处。

所以定义一个数据包,因为要存储接收到的字节数据,因为数据包中有相对应的功能可以提取数据的不同信息

3, 通过socket服务的receive方法将收到的数据存入已定义好的数据包中,

4, 通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台上

5, 关闭资源,

代码如下:

import java.net.*;
class UdpRece
{
	public static void main(String[] args) throws Exception
	{
		//定义UDPsocket服务,建立接收数据的端点
		/*
		但是这两个是接不到的, 由于每个程序都有数字标识,所以收不到, 
		需要让接收端处理的数据都来自发送端的10000端口的, 

		在建立UDP接收服务的时候,通常会监听一个端口,其实就是给这个接受网络应用程序
		定义一个数字标识,

		如果不定义的话, 系统就会自动分配一个数字标识,方便于明确
		那些数据通过该应用程序可以处理;
		*/
		DatagramSocket ds=new DatagramSocket(10000);
				//绑定异常, 地址已经被使用了, 循环一次, 两个服务用了一个端口,所以有问题, 

		while(true){
		//定义一个数据包,因为要存储接收到的字节数据
		byte[] buf=new byte[1024];//创建一个字节数组来接收数据,作为缓冲
		DatagramPacket dp=new DatagramPacket(buf,buf.length);
		
		//通过socket服务的receive方法将收到的数据存入已定义好的数据包dp中,
		ds.receive(dp);//这时就有数据了,在数据包dp中,**此方法是阻塞方法**
						//没数据就等着, 等发完了数据的时候, 就notify , 

		//通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台上
		String ip=dp.<span style="color:#ff6600;">getAddress()</span>.<span style="color:#ff0000;">getHostAddress()</span>;//获取发送过来的ip地址, InetAddress getAddress() 
		String data=new String(dp.getData(),0,dp.getLength());
			//getData() 返回数据缓冲区。getLength() 返回将要发送或接收到的数据的长度。
		int port=dp.getPort();
		System.out.println("ip为:"+ip+".....数据是:"+data+"所发的端口是:"+port);
		}
		//ds.close();
	}
}
结果


但是这里只能发一句,如何写一句发一句呢?就需要用到键盘录入了。IO流, 源变了。

代码如下:

//键盘录入,输入一句,收到一句

import java.net.*;
import java.io.*;
class UdpSend2 
{
	public static void main(String[] args) throws Exception
	{
		DatagramSocket ds=new DatagramSocket();//创建udp服务,建立发送数据的端点,
		BufferedReader bufr=
			new BufferedReader((new InputStreamReader(System.in)));//键盘录入
		String line=null;
		while((line=bufr.readLine())!=null){
			if("over".equals(line)){
				break;
			}
			byte[] buf=line.getBytes();
			DatagramPacket dp=				//确定数据,并封装成数据包,
				//new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.102"),10001);
				new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.255"),10001);
			ds.send(dp);//通过socket服务,将已有的数据包发送出去,通过send方法
		}
		ds.close();
	}
}

class UdpRece2 
{
	public static void main(String[] args) throws Exception
	{
		DatagramSocket ds=new DatagramSocket(10001);
		while(true){
			byte[] buf=new byte[1024];
			DatagramPacket dp=new DatagramPacket(buf,buf.length);
			ds.receive(dp);
			String ip=dp.getAddress().getHostAddress();
			String data=new String(dp.getData(),0,dp.getLength());
			//int port=dp.getPort();
			System.out.println(ip+"....."+data);
		}
	}
}
结果


但是这样只是实现了一端有发, 另一端有接, 现在希望两端都可以接,可以收。

因此需要用到多线程并发执行, 实现Runnable接口,

由于会发生异常, 所以这里在run()方法中, 只能try,不能抛。

代码如下:

/*
	编写一个聊天程序
	有收数据,和发数据部分
	这两个部分要同时执行
	那就需要用到多线程技术
	一个线程控制收,一个线程控制发送
	因为收和发动作是不一致的, 所以定义两个run方法
	而且这两个方法要封装在不同的类中,
*/
import java.net.*;
import java.io.*;
class Send implements Runnable
{
	private DatagramSocket ds;
	Send(DatagramSocket ds){
		this.ds=ds;
	}
	public void run(){
		try
		{
			BufferedReader bufr=
				new BufferedReader(new InputStreamReader(System.in));
			String line=null;
			while((line=bufr.readLine())!=null){
				if("over".equals(line)){
					break;
				}
				byte[] buf=line.getBytes();
				DatagramPacket dp=
					new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.102"),10003);
				ds.send(dp);
			}
		}
		catch (Exception e)
		{
			System.out.println("发送端失败");
		}
	}
}	
class Rece implements Runnable
{	
	private DatagramSocket ds;
	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();
				String data=new String(dp.getData(),0,dp.getLength());
				System.out.println(ip+"...."+data);
			}
		}
		catch (Exception e)
		{
			System.out.println("发送端失败");
		}
		
	}
}
class UdpDemo3

{
	public static void main(String[] args) throws Exception
	{
		new Thread(new Send(new DatagramSocket())).start();
		new Thread(new Rece(new DatagramSocket())).start();
	}
}

TCP传输:

由上面可以了解到, TCP传输是面向连接的, 因此需要首先建立数据传输通道,

1,建立客户端和服务器端(Socket和ServerSocket)

2,建立连接后, 通过Socket中的IO流进行数据的传输

3,关闭socket

同样,客户端与服务器端是两个独立的应用程序



因为tcp是面向连接的,所以在建立socket服务时,就要有服务端存在,并连接成功形成通路后,在该通道进行数据的传输。

一个服务端对应多个客户端, 数据可能发生冲突,

如何完成?

服务端没有流对象, 我拿着你的数据只和你进行通信

如同打电话,

听筒,话筒, 我用我话筒说的东西,你用你的听筒才可以听到,

 

当A客户端发送数据out,  服务端的in在读,(我用我话筒说的东西, 你用你的听筒才可以听到,),然后在用服务端的A的out往回发,

正在通信过程中,

C来了,发请求, 服务端中有个C对象,

图例:

代码如下:

/*
1,tcp 分客户端和服务端
2,客户端对应的对象是Socket
	服务端对应的对象是ServerSocket

客户端:
通过查阅socket对象,发现在该对象建立的时候,就可以去连接指定主机
因为tcp是面向连接的,所以在建立socket服务时,就要有服务端存在,并连接成功
形成通路后,在该通道进行数据的传输

需求:给服务端发送一个文本数据。

*/
import java.net.*;
import java.io.*;
class  TcpClient
{
	public static void main(String[] args) throws Exception
	{
		//创建客户端的socket服务,指定目的主机和端口
		// 如果这个建立成功了,通路有了, 就有了socket流, 这个流中既有输入流, 也有输出流, 
		//在方法中就可以拿到输入流, 和输出流, 来达到对数据的操作 InputStream getInputStream()
		//和OutputStream getOutputStream()
		Socket s=new Socket("192.168.1.102",10004);

		//为了发送数据,应该获取socket流中的输出流
		OutputStream out =s.getOutputStream();

		out.write("tcp is coming...".getBytes());
		s.close();
	}
}
//给服务端发送一个文本数据。并打印在控制台上
class TcpServer
{
	public static void main(String[] args) throws Exception
	{
		//建立服务端ServerSocket服务,并监听一个端口
		ServerSocket ss=new ServerSocket(10004);

		//*****通过accept方法获取连接过来的客户端对象 Socket accept()  
		Socket s=ss.accept();
		
		String ip=s.getInetAddress().getHostAddress();//Socket中的InetAddress getInetAddress()方法 
		System.out.println(ip+"....connected");

		//*****获取客户端发送过来的数据,那么要使用客户端对象的读取流来读取数据
		InputStream in=s.getInputStream();//源是网络流, 这个流来自对像的机器,来自网络

		byte[] buf=new byte[1024];
		int len=in.read(buf);
		System.out.println(new String(buf,0,len));
		
		s.close();//关闭客户端,你跟我连这, 你拿完数据, 还连着,浪费我资源, 
	}
}

结果: 

那么如何来实现TCP传输互访呢?

客户端给服务端发送数据,服务端收到后,给客户端反馈信息,这个反馈信息需要读取

需求:建立一个文本转换服务器

客户端给服务端发送文本,服务端会将文本转成大写在返回给客户端
而且客户端可以不断的进行文本转换,当客户端输入over时,转换结束
分析:
客户端:
既然是操作设备上的数据,那么就可以用io技术,并按照io操作规律来思考
源:键盘录入
目的:网络设备,网络输出流
而且操作的是文本数据,可以选择字符流

步骤:
1,建立服务
2,获取键盘录入
3,将数据发给服务端
4,后去服务端返回的大写数据
5,结束,关资源
都是文本数据, 可以使用字符流进行操作,同时提高效率,加入缓冲。

代码如下:

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

class  TransClient
{
	public static void main(String[] args) throws Exception
	{
		Socket s=new Socket("192.168.1.102",10006);
		//键盘录入
		BufferedReader bufr=
			new BufferedReader(new InputStreamReader(System.in));
		//定义目的,将数据写入到socket输出流,发给服务端
		//BufferedWriter bufOut=
		//	new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

		PrintWriter out=new PrintWriter(s.getOutputStream(),true);

		//定义一个socket读取流,读取服务端返回的大写信息
		BufferedReader bufIn=
			new BufferedReader(new InputStreamReader(s.getInputStream()));
		String line=null;
		while((line=bufr.readLine())!=null){
			
			if("over".equals(line)){
				break;
			}

			//bufOut.write(line);//这里容易出现中断,写出不会读到结束标识,所以没有结束标识
			//bufOut.newLine();//换行
			//bufOut.flush();//刷新
			out.println(line);

			String str=bufIn.readLine();
			System.out.println("from Server..."+str);
		}
		s.close();
		bufr.close();//关闭流资源
	}
}
/*
	服务端:
	源:socket读取流
	目的:socket输出流
	都是文本,装饰
*/
class  TransServer
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss=new ServerSocket(10006);
		Socket s=ss.accept();
		String ip=s.getInetAddress().getHostAddress();
		System.out.println(ip+"....connected");
		//读取Socket读取流中的数据。
		BufferedReader bufIn=
			new BufferedReader(new InputStreamReader(s.getInputStream()));
		//目的:socket输出流,将大写数据写入到Socket输出流,并发送给客户端
		//BufferedWriter bufOut=
		//	new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); 
		PrintWriter out=new PrintWriter(s.getOutputStream(),true);
		
		String line=null;
		
		while((line=bufIn.readLine())!=null){
			System.out.println(line);
			//bufOut.write(line.toUpperCase());
			//bufOut.newLine();//结束标识
			//bufOut.flush();
			out.println(line.toUpperCase());
		}
		s.close();
	}
}
结果:


Tcp上传文件:

代码如下: 

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

class  TextClient
{
	public static void main(String[] args) throws Exception
	{
		Socket s=new Socket("192.168.1.102",10007);

		BufferedReader bufr=
			new BufferedReader(new FileReader("IpDemo.java"));

		//这里老是漏,原因,true, 刷新用的
		PrintWriter out=new PrintWriter(s.getOutputStream(),true);
	
		String line=null;
		while((line=bufr.readLine())!=null){
			out.println(line);
		}

		//out.println("over");//结束标识给服务端
		//如果文本文件中也有这个over, 那就读不完文件了, 
		s.shutdownOutput();//  禁用此套接字的输出流。

		BufferedReader bufIn=
			new BufferedReader(new InputStreamReader(s.getInputStream()));
		String str=bufIn.readLine();
		System.out.println(str);
		bufr.close();
		s.close();
	}
}

class TextServer
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss=new ServerSocket(10007);
		Socket s=ss.accept();
		String ip=s.getInetAddress().getHostAddress();
		System.out.println(ip+".....connected");
		BufferedReader bufIn=
			new BufferedReader(new InputStreamReader(s.getInputStream()));
		PrintWriter out=new PrintWriter(new FileWriter("server.txt"),true);
		String line=null;
		while((line=bufIn.readLine())!=null){
			//if("over".equals(line)){//与客户端的对应起来
			//	break;
			//}
			out.println(line);
		}
		PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
		pw.println("上传成功");
		
		pw.close();
		s.close();
		ss.close();
	}
}
结果:

TCP客户端并发上传图片


客户端: 
1,服务端点
2,读取客户已有的图片数据
3,通过socket输出流, 将数据发给服务端, 
4,读取服务端的反馈信息
5,关闭


这个服务端有个局限性,当A客户端连接上了以后,被服务端获取到, 服务端执行具体流程,
这时B客户端连接, 只有等待,Socket s=ss.accept();
因为服务端还没有处理完A客户端的请求, 
还没有循环回来执行下次accept方法,所以暂时获取不到B客户端对象

那么为了可以让多个客户端同时并发访问服务端,
那么服务端最好的就是将每个客户端封装到一个单独的线程中,
这样,就可以同时处理多个客户端的请求了

如何来定义线程呢?
只要明确了每个客户端要在服务端执行的代码即可,将该代码存入到run方法中,

 代码如下:

import java.io.*;
import java.net.*;
//客户端
class PicClient 
{
	public static void main(String[] args)throws Exception
	{
		if(args.length!=1){
			System.out.println("请选择一个jpg格式的图片");
			break;
		}
		File file=new File(args[0]);
		if(!(file.exists()&&file.isFile())){
			System.out.println("该文件有问题,那么不存在,那么不是文件");
			return;
		}
		if(!(file.getName().endsWith(".jpg"))){
			System.out.println("图片格式错误,请重新选择");
			return;
		}
		if(file.length()>1024*1024*5){
			System.out.println("文件过大,没安好心呀!");
			return;
		}
		//1,服务端点
		Socket s=new Socket("192.168.1.101",10009);
		//2,读取客户已有的图片数据
		FileInputStream fis=new FileInputStream(file);
		OutputStream out=s.getOutputStream();
		byte[] buf=new byte[1024];
		int len=0;
		while((len=fis.read(buf))!=-1){
			out.write(buf,0,len);
		}

		//告诉服务端数据已写完
		s.shutdownOutput();

		InputStream in=s.getInputStream();
		byte[] bufIn=new byte[1024];
		int num=in.read(bufIn);
		System.out.println(new String(bufIn,0,num));
		fis.close();
		s.close();
	}
}

class PicThread implements Runnable
{
	private Socket s;
	PicThread(Socket s){
		this.s=s;
	}
	public void run(){
		int count=1;
		String ip=s.getInetAddress().getHostAddress();
		try
		{
			
			System.out.println(ip+"....connected");
			InputStream in=s.getInputStream();

			File file=new File("E:\\",ip+"("+(count)+")"+".jpg");//为了避免覆盖需要ip和计数器
			while(file.exists()){//用while循环而if只是判断一次。
				file=new File("E:\\",ip+"("+(count++)+")"+".jpg");
			}
			FileOutputStream fos=new FileOutputStream(file);

			byte[] buf=new byte[1024];
			int len=0;
			while((len=in.read(buf))!=-1){
				fos.write(buf,0,len);
			}
			OutputStream out=s.getOutputStream();
			out.write("上传成功".getBytes());
			s.close();
			fos.close();//这里关流, 就刷新了,
		}
		catch (Exception e)
		{
			System.out.println(ip+"上传失败");
		}
	}
}
//服务端
class PicServer
{
	public static void main(String[] args)throws Exception
	{
		ServerSocket ss=new ServerSocket(10009);
		while(true){
			Socket s=ss.accept();
			new Thread(new PicThread(s)).start();
			/*
			一个客户端过来, 被thread对象封装了,这时有2个线程, 同时执行, 
			主线程循环, 这时b客户端亦可以连起来
			*/
		}
	}

}

结果:



Tcp客户端并发登录:

客户端通过键盘录入用户名
服务端对这个用户名进行校验
如果这个用户存在,在服务端显示xxx,已登录
并在客户端显示XXX,欢迎光临
如果该用户不存在,在服务端显示xxx,尝试登陆
并在客户端显示XXX,该用户不存在
最多就登陆三次

代码如下:

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

class LoginClient
{
	public static void main(String[] args) throws Exception
	{
		Socket s=new Socket("192.168.1.102",20001);
		BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
		PrintWriter out=new PrintWriter(s.getOutputStream(),true);
		BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));
		for(int i=0;i<3;i++){
			String line=bufr.readLine();
			if(line==null){
				break;
			}
			out.println(line);
			String info=bufIn.readLine();
			System.out.println("info="+info);
			if(info.contains("欢迎")){
				break;
			}
			
		}
		bufr.close();
		s.close();
	}
}
class UserLogin implements Runnable
{
	private Socket s;
	UserLogin(Socket s){
		this.s=s;
	}
	public void run(){
		String ip=s.getInetAddress().getHostAddress();
		System.out.println(ip+"...connected");
		try
		{
			for(int i=0;i<3;i++){
				BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));
				String name=bufIn.readLine();
				BufferedReader bufr=new BufferedReader(new FileReader("user.txt"));
				PrintWriter out=new PrintWriter(s.getOutputStream(),true);
				String line=null;
				boolean flag=false;
				while((line=bufr.readLine())!=null){
					if(line.equals(name)){
						flag=true;
						break;
					}
				}
				if(flag){
					System.out.println(name+",已登录");
					out.println(name+"欢迎光临");
					break;
				}
				else{
					System.out.println(name+",尝试登陆");
					out.println(name+",有户名不存在");
				}
				bufr.close();
				s.close();
			}
		}
		catch (Exception e)
		{
			throw new RuntimeException(ip+"校验失败");
		}
		
	}
}
class LoginServer
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss=new ServerSocket(20001);
		while(true){
			Socket s=ss.accept();
			new Thread(new UserLogin(s)).start();
		}
	}
}
结果:

1.6 浏览器客户端与自定义服务端

代码如下:

//浏览器
//自定义服务器
//浏览器
//Tomcat服务器

import java.net.*;
import java.io.*;
class ServerDemo
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss=new ServerSocket(11000);
		Socket s=ss.accept();

		System.out.println(s.getInetAddress().getHostAddress());

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

		PrintWriter out=new PrintWriter(s.getOutputStream(),true);
		out.println("<font color='red' size='8'>浏览器客户端你好</font>");

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


首先把服务端开启,

首先在浏览器客户端上,输入

服务端结果:

客户端结果:

1.7 浏览器客户端与tomcat服务器

首先需要把tomcat服务器开启,然后就可以用浏览器访问Tomcat服务器上的资源了,

打开之后, Tomcat服务器的端口号是:

因此:说明Tomcat服务端已经开启,

服务器软件在提供服务,别的浏览器, 也可以访问

Tomcat服务器的好处:可以读取你自定义的资源,

自定义一个网页,放到服务器中的:

然后在浏览器端发出请求, 就可以访问了, 


1.8 自定义浏览器与Tomcat服务端

自己可以定义像浏览器一样,去访问Tomcat,请求服务呢?

那么首先需要明白浏览器向服务器请求什么呢?

由上面可以知道:

这时浏览器给服务器发送的一些消息(请求),Http的请求消息头,

http://192.168.1.101:8080/myweb/demo.html

协议://端口号//资源路径//资源,

那么可以自己弄一个浏览器, 向服务端发送这些数据, 我们也可以自己拿数据

什么都支持

代码如下:

class  MyIE
{
	public static void main(String[] args) throws Exception
	{
		Socket s=new Socket("192.168.1.103",8080);
		PrintWriter out=new PrintWriter(s.getOutputStream(),true);
		out.println("GET /myweb/demo.html HTTP/1.1");//访问这台主机的这个资源路径下的资源
		out.println("Accept: */*");
		out.println("Accept-Language: zh-CN");
		out.println("Host: 192.168.1.102:11000");//访问的是这台主机和端口
		out.println("Connection: keep-alive");
		/*
		一定要写空行, 
		这是数据就发出去了, 到了Tomcat服务器中, 
		然后读回来,
		*/
		out.println();
		out.println();
		
		BufferedReader bufr=
			new BufferedReader(new InputStreamReader(s.getInputStream()));
		String line=null;
		while((line=bufr.readLine())!=null){
			System.out.println(line);
		}
		s.close();
	}
}
结果:

而与浏览器不同的是,浏览器可以解析,并拆包, 但是自定义的浏览器,不可以拆包,并且解析不了,

1.9 自定义图形化界面浏览器,

代码如下:

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;

class MyWinDemo
{
	private Frame f;
	private Button but;
	private TextField tf;
	private TextArea ta;

	//设置对话框
	private Dialog d;
	private Label lab;
	private Button okBut;

	MyWinDemo(){
		 init();
	}
	public void init(){
		f=new Frame("我的窗口");
		f.setBounds(200,100,600,500);
		but=new Button("转到");
		tf=new TextField(60);
		ta=new TextArea(25,70);
		
		d=new Dialog(f,"提示信息-self",true);//模式为true的意思是说, 如果不操作这个对话框, 那么前面的窗体就操作不了,需要点,
		lab=new Label();
		okBut=new Button("确定");
		d.setBounds(400,200,240,150);
		d.setLayout(new FlowLayout());

		f.setVisible(true);
		f.setLayout(new FlowLayout());

		myEvent();
		
		d.add(lab);
		d.add(okBut);


		f.add(tf);
		f.add(but);
		f.add(ta);
		
	}
	public void myEvent(){
		f.addWindowListener(new WindowAdapter(){
			public void windowClosing(WindowEvent e){
				System.exit(0);
			}
		});
		//给对话框窗体监听器
		d.addWindowListener(new WindowAdapter(){
			public void windowClosing(WindowEvent e){
				d.setVisible(false);
			}
		});
		给对话框中按钮活动监听器
		okBut.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
					d.setVisible(false);
			}
		});

		/*在文本框中输入目录下面遍历*/
		but.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				try
				{
					showDir();
				}
				catch (Exception e1)
				{
					throw new RuntimeException("失败");
				}
			}
		});
		//输入完以后, 就可以按回车, 转到文本域中,
		//事件源:文本框,监听器:键盘监听
		tf.addKeyListener(new KeyAdapter(){
			public void keyPressed(KeyEvent e){
				try
				{
					if(e.getKeyCode()==KeyEvent.VK_ENTER)
						showDir();
				}
				catch (Exception e2)
				{
					throw new RuntimeException("失败");
				}
			}
		});
	}
	public void showDir()throws Exception{
		
		ta.setText("");//清空动作

		String url=tf.getText();//   http://192.168.1.103:8080/myweb/demo.html

		int index1=url.indexOf("//")+2;
		int index2=url.indexOf("/",index1);//这两句代码就是截取192.168.1.103:8080

		String str=url.substring(index1,index2);

		String[] arr=str.split(":");
		String host=arr[0];
		int port=Integer.parseInt(arr[1]);

		String path=url.substring(index2);
		ta.setText(str+"...."+path);

		Socket s=new Socket(host,port);
		PrintWriter out=new PrintWriter(s.getOutputStream(),true);
		out.println("GET "+path+" HTTP/1.1");
		out.println("Accept: */*");
		out.println("Accept-Language: zh-CN");
		out.println("Host: "+host+":"+port);//访问的是这台主机和端口
		out.println("Connection: keep-alive");

		out.println();
		out.println();

		BufferedReader bufr=
			new BufferedReader(new InputStreamReader(s.getInputStream()));
		String line=null;
		while((line=bufr.readLine())!=null){
			ta.append(line+"\r\n");
		}
		s.close();

	}
}
class MyIEByGUI
{
	public static void main(String[] args) 
	{
		new MyWinDemo();
	}
}

结果:


无法拆包,因为用的是传输层的协议,

我们就获取到了服务器返回的所有的消息, 并把数据返回到了文本区域中,

解析不了,  浏览器强大就强大在里面挂了很多的解析引擎,HTtp,CSS,javaScript等解析引擎;

但是这个切的比较复杂,java得提供一些对象来解决他,

2.0 URLConnection

浏览器做的事情就是, 将这些消息做了一个解析, 把符合应用层的协议的消息给解析完,拆包去掉了, 把正确的正文数据, 主体部分显示在了,区域范围内,

虽然解析办不了现在, 但是拆包却可以得到,并且不用用切割的方式来获取路径。

 String getFile() 
          获取此 URL 的文件名。 
 String getHost() 
          获取此 URL 的主机名(如果适用)。 
 String getPath() 
          获取此 URL 的路径部分。 
 int getPort() 
          获取此 URL 的端口号。 
 String getProtocol() 
          获取此 URL 的协议名称。 
 String getQuery() 
          获取此 URL 的查询部分。 


Java就提供了一个对象,URL来实现,

而且当中提供了一个方法,可以直接访问服务端,获取这个服务端的链接对象URLConnection openConnection().

这样就内部链接了, 就不需要写Socket ,带着协议,那么现在在应用层,

由于URLConnection 内部封装了socket, 所以就可以直接得到流对象了,

代码只需要将上面的showDir()方法简化即可:

	public void showDir()throws Exception{

		tf.setText("");

		String website=tf.getText();//"http://192.168.1.103:8080/myweb/demo.html"

		URL url=new URL(website);

		//是URLConnection类中的getInputStream()方法
		URLConnection conn=url.openConnection();
		System.out.println(conn);

		InputStream in=conn.getInputStream();

		byte[] buf=new byte[1024];
		int len=in.read(buf);
		ta.setText(new String(buf,0,len));

	}
}
运行结果:

2.1 域名解析(DNS)

为了方便记忆,通常输入的是网址像:

而需要把这个翻译成IP地址再去访问, 到哪去翻译呢?


 DNS叫做域名解析服务器;

首先会在在公网上找DNS,

电信的宽带的话, 如果不配置, 就默认的是电信的DNS服务器,




























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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值