Java基础——网络编程

一、网络传输的前提
 1、确定双方的通信地址:使用IP地址定位一台主机,使用端口号定位一个应用(即一个进程,因此不同的应用在同一个机器上的端口是不能相同的),可以通过域名或者IP地址来获取InetAddress对象:上网的时候若输入的是域名,域名会先发送到DNS(域名解析服务器)上,解析出一个ip地址,将此ip地址返回,再根据ip地址访问网络服务器中的资源

InetAddress inet = InetAddress.getByName("192.168.10.165");
InetAddress inet = InetAddress.getByName("www.baidu.com");//也可用ip地址

//本机网络地址信息
InetAddress localHost = InetAddress.getLocalHost();//获取本机的IP信息
String hostName = localHost.getHostName();//计算机名并非域名
String hostAddress = localHost.getHostAddress();//本机ip

Tip:端口号被规定为一个16位的正数(0-65535)。其中,0-1023被预先定义的服务通信占用(如HTTP占用80端口),除非我们要访问这些特定服务,否则,就应该使用1024-65535这些端口中的某一个进行通信,以免发生端口冲突。端口号与IP地址的组合就是一个网络套接字。
 2、可靠、高效的数据传输:依靠网络通信协议
在这里插入图片描述
 IP(Internet Protocol)协议是网络层的主要协议,支持网间互连的数据通信。网络通信可看作是数据封装和拆封的过程,很像寄送快递的过程,要有地址、要包装,到了以后又要拆开包装,拿到快递。

二、传输层协议TCP和UDP
 1、TCP(传输控制协议)协议:适用于数据要求较高的场景,几乎不会丢失数据包,基于Socket和ServerSocket编程
  ①使用TCP,须先建立TCP连接,形成数据传输通道
  ②数据传输前,会进行“三次握手”,是可靠的
  ③数据传输完毕,需释放已建立的连接,效率低
 2、UDP(用户数据报协议)协议:适用于数据要求较低的场景,可能会丢失数据包,比如影视类的数据就可以采用UDP协议传输,基于DatagramSocket和DatagramPacket编程
  ①将数据、源、目的封装成数据包,不需要事先建立连接,但不可靠(有时会丢包)
  ②每个数据包的大小限制在64K内
  ③数据发送完毕无需释放资源,效率高

三、Socket
 网络套接字(Socket):是IP和端口号的组合,java中有对应的Socket类,可以创建Socket对象。利用套接字(Socket)开发网络应用程序早已被广泛采用,以至于成为事实上的标准。通信的两端都要都Socket,是两台机器间通信的端点,网络通信其实就是Socket间的通信。Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。
 客户端传输数据步骤:
  ①创建Socket对象(ip地址,端口号):

Socket socket = new Socket(InetAddress,port);//一定要指明IP地址和端口号

  ②获取输出流对象,以往外传输数据:

OutputStream os = socket.getOutputStream();

  ③通过写的方式传递数据:

os.write(“hello,world!.getBytes());

  ④关闭流和Socket:

os.close();
socket.close();

 服务端接收数据步骤:
  ①通过端口号创建ServerSocket对象:

ServerSocket ss = new ServerSocket(port);//此端口号要与客户端一致

  ②再通过ServerSocket对象的accept()方法获取Socket对象:

Socket s = ss.accept();

  ③通过Socket对象获取InputStream对象:

InputStream is = s.getInputStream();

  ④利用InputStream对象读取客户端传输过来的内容,方式与IO流中的读取方式相同
  ⑤关闭所有的资源:

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

注意:先开服务端,再开客户端,否则会中断连接
 示例1:客户端与服务端的简单通信

public class TestCS {
	@Test
	public void client() {
		Socket socket = null;
		OutputStream os = null;
		InputStream is = null;
		InputStreamReader isr = null;
		try {
			socket = new Socket("127.0.0.1", 9090);
			os = socket.getOutputStream();
			os.write("这里是空中一号,请求迫降!".getBytes());
			// 告诉服务端输出完毕,否则会引起阻塞
			socket.shutdownOutput();// 这句话很重要,否则会引起阻塞
			is = socket.getInputStream();//获取服务端的响应信息
			isr = new InputStreamReader(is);
			char[] b = new char[10];
			int len;
			while ((len = isr.read(b)) != -1) {
				String str = new String(b, 0, len);
				System.out.print(str);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (isr != null) {
				try {
					isr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (os != null) {
				try {
					os.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (socket != null) {
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	@Test
	public void server() {
		ServerSocket ss = null;
		Socket s = null;
		InputStream is = null;
		InputStreamReader isr = null;
		OutputStream os = null;
		try {
			ss = new ServerSocket(9090);
			s = ss.accept();
			is = s.getInputStream();//获取客户端的请求信息
			isr = new InputStreamReader(is);
			char[] b = new char[10];
			int len;
			while ((len = isr.read(b)) != -1) {
				String str = new String(b, 0, len);
				System.out.print(str);
			}
			System.out.println();
			System.out.println("收到来自:" + s.getInetAddress().getHostAddress() + "的请求。");
			os = s.getOutputStream();
			os.write("已收到你的请求,可以迫降!".getBytes());//给客户端的响应
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (isr != null) {
				try {
					isr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (os != null) {
				try {
					os.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (s != null) {
				try {
					s.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (ss != null) {
				try {
					ss.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

注意:节点流中的read()方法是阻塞式的,在没有内容可读的时候会一直等待,当在读取客户端传输的数据时,由于不知道数据什么时候传输完毕,就会一直等待读取数据,就会引起阻塞,所以发送数据的那一端要调用socket.shutdownOutput();方法知会另一端,告诉它数据已传输完毕,以避免阻塞。缓冲流(Buffered)中的read()方法是非阻塞式的。
 示例2:客户端从本地读取文件并传输到服务端,服务端收到文件并下载到本地

public class DownloadTest {
	@Test
	public void client() throws Exception {
		Socket socket = new Socket("127.0.0.1", 9797);
		OutputStream os = socket.getOutputStream();
		FileInputStream fis = new FileInputStream("07.jpg");
		byte[] b = new byte[20];// 此数组可供该方法中的多个流公用
		int len;
		while ((len = fis.read(b)) != -1) {
			os.write(b, 0, len);
		}
		socket.shutdownOutput();
		InputStream is = socket.getInputStream();
		while ((len = is.read(b)) != -1) {
			String str = new String(b, 0, len);
			System.out.print(str);
		}
	}

	@Test
	public void server() throws Exception {
		ServerSocket ss = new ServerSocket(9797);
		Socket socket = ss.accept();
		InputStream is = socket.getInputStream();
		FileOutputStream fos = new FileOutputStream("1.jpg");
		byte[] b = new byte[30];
		int len;
		while ((len = is.read(b)) != -1) {
			fos.write(b, 0, len);
		}
		System.out.println("收到来自:" + socket.getInetAddress().getHostAddress() + "发来的图片.");
		OutputStream os = socket.getOutputStream();
		os.write("你发送的图片已收到".getBytes());
		socket.shutdownOutput();
	}
}

四、DatagramSocket
 Socket和ServerSocket是基于TCP协议的,UDP协议下使用的是DatagramSocket和DatagramPacket。
 UDP数据报通过数据报套接字(DatagramSocket)发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不确定什么时候可以抵达。
 DatagramPacket对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号。UDP协议中每个数据报都给出了完整的地址信息,因此无需建立发送方和接收方的网络连接。
 UDP发送信息的步骤:
  ①创建DatagramSocket(数据报套接字)的对象:

DatagramSocket ds = new DatagramSocket();

  ②创建DatagramPacket(数据报)的对象:

//携带数据和目的地的IP和端口号
DatagramPacket dp = new DatagramPacket(b,0,len,address,port);

  ③调用DatagramSocket对象的send()方法发送数据:

ds.send(dp);//发送的即为DatagramPacket数据报

  ④关闭套接字对象:

ds.close();

 UDP接收信息的步骤:
  ①根据发送端的端口号创建DatagramSocket的对象:

DatagramSocket ds = new DatagramSocket(port);//该端口号要与发送端的一致

  ②创建一个字节数组,用来存放接收到的数据:

byte[] b = new byte[1024];

  ③以字节数组为参数创建一个数据报对象保存数据:

//不需要ip和端口号,因为是接收数据,不需要发送出去
DatagramPacket dp = new DatagramPacket(b,0,b.length);

  ④调用套接字对象的receive()方法,接收数据:

//由于端口号和发送端的相同,因此接收到的数据肯定是来自发送端的,将接收来的数据保存在dp对象中
ds.recieve(dp);

  ⑤读取数据:

String str = new String(dp.getData(),0,dp.getLength());

  ⑥打印输出:

System.out.println(str);

 示例:在接收字符串的时候,有可能定义的字节数组的长度不够,而只能接收到一部分传送过来的字符串而导致数据丢失,这时可以另外定义一个字符串,让每次读取的字符串都加在它身上

public class UDPTest {
	@Test
	public void send() {
		DatagramSocket ds = null;
		try {
			ds = new DatagramSocket();
			byte[] b = "我是发送端...".getBytes();
			DatagramPacket pack = new DatagramPacket(b, 0, b.length, InetAddress.getByName("127.0.0.1"), 9090);
			ds.send(pack);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			ds.close();
		}
	}

	@Test
	public void recieve() {
		DatagramSocket ds = null;
		try {
			ds = new DatagramSocket(9090);
			byte[] b = new byte[1024];
			DatagramPacket dp = new DatagramPacket(b, 0, b.length);
			ds.receive(dp);
			String str = new String(dp.getData(), 0, dp.getLength());
			System.out.println(str);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			ds.close();
		}
	}
}

 注意:要先开接收端,否则虽然不抛异常但接收不到数据

五、URL
 URL(Uniform Resource Locator),统一资源定位符,它表示Internet上某一资源的地址。通过URL我们可以访问Internet上的各种网络资源,比如常见的www、ftp(文件传输协议-File Transfer Protocol)站点。浏览器通过解析给定的URL可以在网络上查找相应的文件或其他资源。
 URL的基本结构:<传输协议>://<主机名>:<端口号>/<文件名>,例如:http://192.168.1.100:8080/hello/index.jsp。
 和File对象对应一个硬盘中的文件类似,一个URL的对象对应一个网络中的资源:

URL url = new URL("http://127.0.0.1:8080/examples/Hello.txt");

 URL类的构造方法都抛出非运行时异常,通常使用try-catch进行捕获处理。一个URL对象生成后,其属性是不能被改变的,可以通过它给定的方法来获取这些属性的值,相应的方法如下:

public String getProtocol();//获取URL的协议名称
public String getHost();//获取URL的主机名
public String getPort();//获取URL的端口号
public String getPath();//获取URL的文件路径
public String getFile();//获取URL的文件名
public String getRef();//获取URL在文件中的相对位置
public String getQuery();//获取URL的查询字符串

 URL的openStream()方法返回一个InputStream,可以将网络上数据读取到内存,当然,读取到内存后也可以使用输出流输出;URL还提供了一种输出数据的方式,通过URLConnection先与URL建立连接,然后对其进行读写。
 URLConnection表示到URL所引用的远程对象的连接,当与一个URL建立连接时,首先要一个URL对象通过openConnection()方法生成对应的URLConnection对象,如果连接失败,将抛出IOException异常:

URL url = new URL("http://www.bdm.com/index.jsp");
URLConnection uc = url.openConnection();

 从网络上获取资源的步骤:
  ①创建URL的对象:

URL url = new URL("http://127.0.0.1:8080/examples/Hello.txt");

  ②用URL对象调用其openStream()方法:

InputStream is = url.openStream();//获取输入流

  ③读数据:和之前的流读数据时一样,如果需要将该文件保存下来,可以再将读取的内容写成一个对应格式的文件

byte[] b = new byte[20];
int len; 
while(){
	//……
}

  ④关闭流:

is.close();

 也可以通过URLConnection对象获取输入流和输出流,实现和现有的CGI程序交互:

public Object getContent()
public int getContentLength()
public String getContentType()
public long getDate()
public long getLastModified()
public InputStream getInputStream()
public OutputStream getOutputStream()

 示例:网络下载资源

public class NetDownload {
	public static void main(String[] args) throws Exception {
		URL url = new URL("http://127.0.0.1:8080/examples/Hello.txt");
		// 从控制台输出网上的资源
		InputStream is = url.openStream();
		InputStreamReader isr = new InputStreamReader(is, "GB2312");// 读取的格式要和写入的一致
		char[] c = new char[10];
		int len;
		String str = "";
		while ((len = isr.read(c)) != -1) {
			String s = new String(c, 0, len);
			str += s;
		}
		System.out.println(str);
		is.close();
		System.out.println("----------------------------");
		// 将文件下载到本地
		URLConnection openConnection = url.openConnection();
		InputStream is1 = openConnection.getInputStream();
		FileOutputStream fos = new FileOutputStream("hello.txt");
		byte[] b = new byte[10];
		int len1;
		while ((len1 = is1.read(b)) != -1) {
			// String str1 = new String(b);
			// fos.write(b.toString().getBytes());
			fos.write(b, 0, len1);
		}
		fos.close();
		is1.close();
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值