黑马程序员—Java网络编程

13. 网络编程

 

 

13.1. 概述

 

数据要发送到对方指定的应用程序上,为了标识这些应用程序,所以给这些网络应用程序都用数字进行标识。为了方便称呼这个数字,叫做端口。

 

端口范围: 0~65535   系统一般占用0~1024

 

网络模型:

OSI参考模型 :将网络分成七层(应用层、表示层、会话层、传输层、网络层、数据链路

层、物理层)

TCP/IP参考模型 :将网络分成四层(应用层、传输层、网际层、主机至网络层)

 

java网络编程通常是在网际层和传输层,每一层都有自己对应的协议,网际层是IP协议,传输层是TCP协议,应用层是HTTP和FTP协议。


13.2 . IP地址

 

IP地址:是网络中设备的标识,不易记忆,可用主机名。本地回环地址:127.0.0.1  主机名localhost。

import java.net.*;
import java.util.*;
class  IPDemo
{
	public static void main(String[] args) throws Exception
	{
		//返回本地主机的对象
		//InetAddress i = InetAddress.getLocalHost();
		//System.out.println(i.toString());
		//System.out.println("address:"+i.getHostAddress());
		//System.out.println("name:"+i.getHostName());

		//获取给定主机名的IP地址
		//InetAddress ia = InetAddress.getByName("www.baidu.com");
		//System.out.println("address:"+ia.getHostAddress());
		//System.out.println("name:"+ia.getHostName());

		//获取给定主机名的所有IP地址
		InetAddress[] ia = InetAddress.getAllByName("www.baidu.com");
		System.out.println(Arrays.toString(ia));
	}
}


13.3 . 传输协议TCP和UDP

 

TCP:

1)建立连接,形成传输数据的通道

2)在连接中进行大数据量传输

3)通过三次握手完成连接,是可控协议

4)必须建立连接,效率会稍低

对方必须在线。先确认对方是否在,通过三次握手(发送确认信息,对方返回确认信息,再发送已接收确认信息),在通道中传输,如果单方面断开则停止传送数据,像电话。主要用于如下载文件,注重安全。

 

UDP:

1) 将数据及源和目的封装成数据包中,不需要建立连接

         2)每个数据包的大小限制在64k内

         3)因无连接,是不可靠协议

         4)不需要建立连接,速度快

         即不论接收方是否存在或者在线,都能够发出去,如果没有接收到就会丢失,像对讲机。主要用于如聊天软件,视频会议,桌面共享,注重速度。


13.4 . Socket

 

Socket就是为网络服务提供的一种机制,通信的两端都有Socket,网络通信其实就是Socket间的通信,数据在两个Socket间通过IO传输。Socket就是计算机中的网络应用程序。


13.5 . UDP传输

 

 

在创建DatagramSocketDatagramPacket对象时,会报出UnknownHostException异常,因为IP地址和端口都有可能出错

import java.net.*;
import java.util.*;
//发送端
class  UdpSend
{
	public static void main(String[] args) throws Exception
	{
		//1.创建udp服务,通过DatagramSocket对象
		DatagramSocket ds = new DatagramSocket(8888);

		//2.确定数据,并封装成数据包
		byte[] buf = "udp I am coming".getBytes();
		DatagramPacket dp = new DatagramPacket(buf,buf.length,
								InetAddress.getByName("127.0.0.1"),10000);
	
		//3.通过socket服务,将已有的数据包发送出去,通过send方法
		ds.send(dp);

		//4.关闭资源
		ds.close();
	}
}
//接收端
class  UdpRece
{
	public static void main(String[] args) throws Exception
	{
		//1.创建udp socket,建立端点
		/*监听一个端口,其实就是给这个接收网络应用程序定义数字标识
		  方便于明确哪些数据过来该应用程序可以处理。*/
		DatagramSocket ds = new DatagramSocket(10000);

		//2.定义一个数据包,用于存储数据
		byte[] buf = new byte[1024];
		DatagramPacket dp = new DatagramPacket(buf,buf.length);

		//3.通过socket服务的receive方法将收到的数据存入已定义好的数据包中
		//该方法是阻塞式的,没有数据就等待。
		ds.receive(dp);

		//4.通过数据包的方法获取其中的数据
		String ip = dp.getAddress().getHostAddress();
		String data = new String(dp.getData(),0,dp.getLength());
		int port = dp.getPort();
		System.out.println(ip+"::"+data+"::"+port);

		//5.关闭资源。
		ds.close();
	}
}


13.6 . TCP传输

 

TCP分客户端和服务端,分别对应的是Socket和ServeSocket

 

客户端:

通过查阅Socket对象,发现在该对象建立时,就可以去连接指定主机。因为tcp是面向连接的,所以在建立Socket服务时,就要有服务端存在,并连接成功,形成通路后,在该通道进行数据的传输。Socket流中既有输入流,也有输出流。

//从客户端向服务端发送信息:
import java.io.*;
import java.net.*;
class  TcpClient
{
	public static void main(String[] args) throws Exception
	{
		//1.创建Socket服务,并指定要连接的主机和端口
		Socket s = new Socket("192.168.1.105",10003);
		//2.获取socket流中的输出流,用来发送数据
		OutputStream out = s.getOutputStream();

		out.write("tcp i am coming".getBytes());
		s.close();
	}
}
class TcpServer
{
	public static void main(String[] args) throws Exception
	{
		//1.建立服务端Socket服务,ServerSocket(),并监听一个端口
		ServerSocket ss = new ServerSocket(10003);
		//2.获取连接过来的客户端对象,通过accept方法,这个方法是阻塞式的
		Socket s = ss.accept();
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip);
		//3.获取该客户端对象的读取流来读取发过来的数据
		InputStream in = s.getInputStream();
		byte[] buf = new byte[1024];
		int len = in.read(buf);
		System.out.println(new String(buf,0,len));
		//4.关闭客户端
		s.close();
		ss.close();
	}
}
//运行时需要先运行服务端。


通过tcp,将客户端输入的字符串传给服务端后,转换成大写传输回来:

import java.io.*;
import java.net.*;
class  TcpClient2
{
	public static void main(String[] args) throws Exception
	{
		Socket s = new Socket("192.168.1.105",10004);
		//定义读取键盘数据的流对象
		BufferedReader bufr = 
			new BufferedReader(new InputStreamReader(System.in));
		//定义目的,将数据写入到socket输出流,发给服务端
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
		/*
		BufferedWriter bufOut = 
			new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
		*/
		//定义一个socket输入流,读取服务端返回的大写信息
		BufferedReader bufIn = 
			new BufferedReader(new InputStreamReader(s.getInputStream()));
		String line = null;
		while ((line=bufr.readLine())!=null)
		{
			//输入over跳出循环socket结束,此时服务器readLine会读取到-1,也跳出
			if("over".equals(line))
				break;
			out.println(line);
			/*
			bufOut.write(line);
			//利用BufferedWriter时,write语句并没有在后面加上回车符,所以要添加上
			bufOut.newLine();
			bufOut.flush();
			*/
			String str =  bufIn.readLine();
			System.out.println(str);
		}
		bufr.close();
		s.close();
	}
}
class TcpServer2
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss = new ServerSocket(10004);
		Socket s = ss.accept();
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip);
		//读取socket读取流中的数据
		BufferedReader bufIn = 
			new BufferedReader(new InputStreamReader(s.getInputStream()));
		//将大写数据写入到socket输出流,并发送给客户端
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
		/*
		BufferedWriter bufOut = 
			new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
		*/
		String line = null;
		while ((line=bufIn.readLine())!=null)
		{	
			out.println(line.toUpperCase());
			/*
			bufOut.write(line.toUpperCase());
			bufOut.newLine();
			bufOut.flush();
			*/
		}
		s.close();
		ss.close();
	}
}

该例子出现的问题:

现象:客户端和服务端都在莫名的等待

原因:因为客户端和服务端都有阻塞式的方法,这些方法没有读到结束标记,就会一直等待,而导致两端都在等待。


13.7 .URL类

 

常用方法:

String  getFile()          获取此 URL 的文件名

String  getHost()         获取此 URL 的主机名(如果适用)。

String  getPath()         获取此 URL 的路径部分。

int  getPort()                获取此 URL 的端口号。

String  getProtocol()      获取此 URL 的协议名称。

String  getQuery()        获取此 URL 的查询部分。

 

创建URL对象时,会抛出MalformedURLException异常,因为传入的字符串可能不符合格式

import java.net.*;
class URLDemo 
{
	public static void main(String[] args) throws MalformedURLException
	{
		//URL url = new URL("http://127.0.0.1:8080/myweb/demo.html");
		URL url = 
<span style="white-space:pre">			</span>new URL("http://127.0.0.1:8080/myweb/demo.html?name=haha&age=30");

		System.out.println("getProtocol() :"+url.getProtocol());
		System.out.println("getHost() :"+url.getHost());
		System.out.println("getPort() :"+url.getPort());

		System.out.println("getgetPath() :"+url.getPath());输出 /myweb/demo.html

		System.out.println("getFile() :"+url.getFile());输出 /myweb/demo.html?name=haha&age=30

		System.out.println("getQuery() :"+url.getQuery());输出 name=haha&age=30

		//如果url中没有端口号,会赋予默认的80端口号
		int port = url.getPort();
		if(port==-1)
			port = 80;
	}
}

特殊方法:

URLConnection openConnection()           

使用此方法会在内部创建Socket对象,并发送相关的响应头到服务端,获取想要的网页内容

 

URLConnection对象

通过url的openConnection方法获得此对象,会抛出IOException异常

此对象内包含了Socket对象,无需再建立Socket对象

import java.net.*;
import java.io.*;
class  URLConnectionDemo
{
	public static void main(String[] args) throws Exception
	{
		URL url = new URL("http://127.0.0.1:8080/myweb/demo.html");
		URLConnection conn = url.openConnection();
		//通过URLConnection获取的输入流进行读取,可以将服务端返回的响应头去掉
		InputStream in = conn.getInputStream();
		byte[] buf = new byte[1024];
		int len = in.read(buf);
		System.out.println(new String(buf,0,len));
	}
}

通过URL类建立自定义图形化浏览器:

import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
class  MyIEByGUI2
{
	private Frame f;
	private TextField tf;
	private TextArea ta;
	private Button but;

	MyIEByGUI2()
	{
		init();
	}
	public void init()
	{
		f = new Frame("MyIE");
		f.setBounds(750,100,500,550);
		f.setLayout(new FlowLayout());
		tf = new TextField(50);
		ta = new TextArea(30,60);
		but = new Button("转到");
		f.add(tf);
		f.add(but);
		f.add(ta);
		myevent();
		f.setVisible(true);
	}
	public void myevent()
	{
		f.addWindowListener(new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				System.exit(0);
			}
		});
		but.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				try
				{
					getWeb();
				}
				catch (Exception exc)
				{
				}
			}
		});
		tf.addKeyListener(new KeyAdapter()
		{
			public void keyPressed(KeyEvent e)
			{
				try
				{
					if(e.getKeyCode()==KeyEvent.VK_ENTER)
						getWeb();
				}
				catch (Exception exc)
				{
				}
			}
		});
	}
	private void getWeb() throws Exception
	{
		ta.setText("");
		String urlPath = tf.getText();
		//利用URL类来创建连接
		URL url = new URL(urlPath);
		URLConnection conn = url.openConnection();
		InputStream in = conn.getInputStream();
		byte[] buf = new byte[1024];
		int len = in.read(buf);
		ta.setText(new String(buf,0,len));
	}
	public static void main(String[] args) 
	{
		new MyIEByGUI2();
	}
}


13.8 .小知识点

 

InetAddress将IP地址进行了封装

InetSocketAddress将IP地址和端口号进行了封装

 

建立Socket()空参数对象,通过connect(InetSocketAddress)可以进行连接

 

 

ServerSocket(intport, int backlog)  中backlog是支持同时在线的客户端数量

 

 

13.9 .域名解析

 

如果url中输入的是主机名(网址),先走的是本地的C:\Windows\System32\drivers\etc\hosts文件,来找是否有对应的映射关系,没有再走公共的DNS服务器查找相应的映射关系,再将找到的IP地址返回给本机去建立连接。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值