Java基础(十三)——网络编程



1,概念

1)网络协议
        计算机网络协议通常由三部分组成:一是语义部分,用于决定双方对话的类型;二是语法部分,用于决定双方对话的格式;三是交换规则,用于决定通信双方的应答关系。

2)IP
         IP地址用于标志网络中的一个通信实体,这个通信实体可以是一台主机,也可以是一台打印机,或者是路由器的某一个端口。而在基于IP协议网络中传输的数据包,都必须使用IP地址来进行标识。IP地址可以唯一的确定网络上的一个通信实体,但一个通信实体可以有多个程序同时提供网络服务,此时还需要端口。
       本机IP地址:localhost 127.0.0.1

3)端口
        端口是一个16位的整数,用于表示数据交给哪个通信程序处理。因此,端口就是应用程序与外界交流的出入口。
        不同的应用程序处理不同的端口上的数据,同一台机器上不能有两个程序使用同一个端口,端口号可以从0到65535。

4)UDP和TCP
         UDP:面向无连接的传输协议。UDP是User Datagram Protocol的简称, 中文名是用户数据包协议。它的特点是将数据及源和目的封装成数据包,每个数据包很小,限制在64K以内,不需要建立连接,速度快,不可靠。
        TCP:面向连接的传输协议。TCP是Transmission Control Protocol的简称,中文名是传输控制协议。它的特点是需要建立连接,形成数据传输通道,可以进行大量数据的传输,通过三次“握手”完成连接,是可靠协议,但是效率稍低。

5)Socket
        所谓socket通常也称作“套接字”,应用程序通常通过“套接字”向网络发出请求或者应答网络请求。其实就是为网络服务提供的一种机制。通信两端都有Socket,网络通信就是Socket间的通信,数据在两个Socket间通过IO传输。

2,Java的基本网络支持
2.1,InetAddress类
        Java提供了InetAddress类来代表IP地址,InetAddress下还有2个子类:Inet4Address、Inet6Address,分别代码IPv4和IPv6的地址。
此外,InetAddress类没有提供构造器,而是对外提供了方法来获取其对象,以下是InetAddress的常用方法:
static InetAddress getByAddress(String host, byte[] addr):根据提供的主机名和IP地址创建InetAddress。
static InetAddress getByName(String host):在给定主机名的情况下确定主机的IP地址。
static InetAddress getLocalHost():返回本地主机。
String getCanonicalHostName():获取此IP地址的完全限定域名。
String getHostAddress():返回IP地址字符串(以文本表现形式)。
String getHostName():获取此IP地址的主机名。
boolean isReachable(int timeout):测试是否可以达到该地址。

2.2,URLDecoder和URLEncoder
        URLDecoder和URLEncoder用于完成普通字符串和application/x-www-form-urlencoded MIME字符串之间的相互转换。
URLDecoder的方法:
URLDecoder():构造方法
static String decode(String s, String enc):使用指定的编码机制对application/x-www-form-urlencoded字符串解码。
URLEncoder的方法:
static String encode(String s, String enc):使用指定的编码机制将字符串转换为application/x-www-form-urlencoded格式。

2.3,URL和URLConnection
        URL类提供了多个构造器用于创建URL对象:
URL(String spec):根据String表示形式创建URL对象。
URL(String protocol, String host, int port, String file):根据指定protocol、host、port号和file创建URL对象。
URL(String protocol, String host, int port, String file, URLStreamHandler handler):根据指定的protocol、host、port号、file和handler创建URL对象。
URL(String protocol, String host, String file):根据指定的protocol名称、host名称和file名称创建URL。
URL(URL context, String spec):通过在指定的上下文中对给定的spec进行解析创建 URL。
URL(URL context, String spec, URLStreamHandler handler):通过在指定的上下文中用指定的处理程序对给定的spec进行解析来创建URL。

         一旦获得了URL对象之后,可以调用如下方法来访问该URL对应的资源:
String getFile():获取此URL的文件名。
String getHost():获取此URL的主机名(如果适用)。
String getPath():获取此URL的路径部分。
int getPort():获取此URL的端口号。
String getProtocol():获取此URL的协议名称。
String getQuery():获取此URL的查询部分。
URLConnection openConnection():返回一个URLConnection对象,它表示到URL所引用的远程对象的连接。
InputStream openStream():打开到此URL的连接并返回一个用于从该连接读入的InputStream。
       
        URL的openConnection()方法将返回一个URLConnection对象,该对象表示应用程序和URL之间的通信连接。程序可以通过URLConnection实例向该URL发送请求、读取URL引用的资源。通常创建一个和URL的连接,并发送请求、读取此URL引用的资源需要如下几个步骤:
1)通过调用URL对象的openConnection()方法来创建URLConnection对象。
2)设置URLConnection的参数和普通请求属性。
3)如果只是发送GET方式请求,使用connect方法建立和远程资源之间的实际连接即可;如果需要发送POST方式的请求,需要获取URLConnection实例对应的输出流来发送请求参数。
4)远程资源变为可用,程序可以访问远程资源的头字段或通过输入流读取远程资源的数据。
5)在建立和远程实际连接之前,程序可以通过URLConnection实例提供的方法设置请求头字段;当资源可用之后,程序可以调用URLConnection实例的方法来访问头字段和内容。

        URLConnection类是一个抽象类,提供了一个空构造器,但是要换取该类的实例,只能靠URL类的openConnection()方法获取。
常用设置方法:
void setAllowUserInteraction(boolean allowuserinteraction):设置此URLConnection的allowUserInteraction字段的值。
void setDoInput(boolean doinput):将此URLConnection的doInput字段的值设置为指定的值。
void setDoOutput(boolean dooutput):将此URLConnection的doOutput字段的值设置为指定的值。
void setIfModifiedSince(long ifmodifiedsince):将此URLConnection的ifModifiedSince字段的值设置为指定的值。
void setUseCaches(boolean usecaches):将此URLConnection的useCaches字段的值设置为指定的值。
void setRequestProperty(String key, String value):设置一般请求属性。
void addRequestProperty(String key, String value):添加由键值对指定的一般请求属性。
常用获取方法:
Object getContent():获取此URL连接的内容。
String getHeaderField(String name):返回指定的头字段的值。
InputStream getInputStream():返回从此打开的连接读取的输入流。
OutputStream getOutputStream():返回写入到此连接的输出流。
String getContentEncoding():返回content-encoding头字段的值。
int getContentLength():返回content-length头字段的值。
String getContentType():返回content-type头字段的值。
long getDate():返回date头字段的值。
long getExpiration():返回expires头字段的值。
long getLastModified():返回last-modified头字段的值。

3,UDP网络编程

        UDP协议是一种不可靠的网络协议,它在通信实例的两端各建立一个Socket,但这两个Socket之间并没有虚拟链路,这两个Socket只是发送、接收数据报的对象,Java提供了DatagramSocket作为基于UDP协议的Socket,使用DatagramPacket代表DatagramSocket发送、接收的数据报。
1)DatagramSocket
        DatagramSocket不维护状态,不产生IO流,它的作用就是接收和发送数据报,Java使用DatagramPacket来代表数据报,DatagramSocket接收和发送的数据都是通过DatagramPacket对象来完成的。
构造方法:
DatagramSocket():构造数据报套接字并将其绑定到本地主机上任何可用的端口。
DatagramSocket(int port):创建数据报套接字并将其绑定到本地主机上的指定端口。
DatagramSocket(int port, InetAddress laddr):创建数据报套接字,将其绑定到指定的本地地址。
常用方法:    
void receive(DatagramPacket p):从此套接字接收数据报包。
void send(DatagramPacket p):从此套接字发送数据报包。
void close():关闭此数据报套接字。
InetAddress getInetAddress():返回此套接字连接的地址。
InetAddress getLocalAddress():获取套接字绑定的本地地址
int getLocalPort():返回此套接字绑定的本地主机上的端口号。  
int getPort():返回此套接字的端口。
         从上面的receive和send方法可以看出,使用DatagramSocket发送数据报时,DatagramSocket并不知道将该数据报发送到哪,而是由DatagramPacket自身决定数据报的目的,DatagramPacket包含了发送目的地。

2)DatagramPacket
构造方法:
DatagramPacket(byte[] buf, int length)
构造 DatagramPacket,用来接收长度为length的数据包。
DatagramPacket(byte[] buf, int offset, int length)
构造 DatagramPacket,用来接收长度为length的包,在缓冲区中指定了偏移量。
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
构造数据报包,用来将长度为length的包发送到指定主机上的指定端口号。
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
构造数据报包,用来将长度为length偏移量为offset的包发送到指定主机上的指定端口号。
        在接收数据报前,应该采用前面2个构造方法生成一个DatagramPacket对象,给出接收数据的字节数组及其长度。然后调用DatagramSocket的方法receive()等待数据报的到来,receive()将一直等待(阻塞),直到收到一个数据报为止。如

//创建接收数据的DatagramPacket对象
DatagramPacket packet = new DatagramPacket(buf,1024);
//接收数据
socket.receive(packet);
        发送数据之前,调用后面2个构造方法创建DatagramPacket对象,此时的字节数组里存放了想发送的数据。除此之外,还要给出目的地址,包括IP地址和端口号。发送数据是通过DatagramSocket的方法send()实现的,send根据数据报的目的地址寻径以传递数据报,如

//创建一个发送数据的DatagramPacket对象
DatagramPacket packet = new DatagramPacket(buf,length,address,port);
//发送数据
socket.send(packet);
常用方法:
InetAddress getAddress():返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。
byte[] getData():返回数据缓冲区。
int getLength():返回将要发送或接收到的数据的长度。
int getPort():返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。
SocketAddress getSocketAddress():获取要将此包发送到的或发出此数据报的远程主机的 SocketAddress(通常为 IP 地址 + 端口号)。
void setAddress(InetAddress iaddr):设置要将此数据报发往的那台机器的 IP 地址。
void setData(byte[] buf):为此包设置数据缓冲区。
void setData(byte[] buf, int offset, int length):为此包设置数据缓冲区。
void setLength(int length):为此包设置长度。
void setPort(int iport):设置要将此数据报发往的远程主机上的端口号。

3)演示示例
发送端
需求:通过udp传输方式,将一段文字数据发送出去
思路:
1,建立UDP的Socket服务
2,提供数据,并将数据封装到数据包中
3,通过Socket服务的发送功能,将数据包发出去
4,关闭资源
接收端
需求:定义一个程序,用于接收udp协议传输的数据并处理
思路:
1,定义UDP的Socket服务,通常会监听一个端口,其实就是给这个接收端程序定义数字标识。方便于明确哪些发送过来的数据可以被程序处理
2,定义一个数据包,因为要存储接收到的字节数据,数据包对象中有更多功能可以提取字节数据中的不同数据信息
3,通过Socket服务的receive方法将收到的数据存入已定义好的数据包中
4,通过数据包对象的特有功能,将这些不同的数据取出,并打印在控制台上
5,关闭资源

import java.net.*;
class UDPSend {
	public static void main(String[] args) throws Exception {
		// 通过DatagramSocket对象,建立UDP服务
		DatagramSocket ds = new DatagramSocket();
		// 确定数据
		byte[] buf = "zhe shi yi duan shu ju".getBytes();
		// 将数据封包。DatagramPacket(byte[] buf, int length, InetAddress address, int
		// port)
		DatagramPacket dp = new DatagramPacket(buf, buf.length,
				InetAddress.getByName("192.168.1.7"), 10000);
		// 发送
		ds.send(dp);
		// 关闭资源
		ds.close();
	}
}
class UDPRece {
	public static void main(String[] args) throws Exception {
		// 创建UDP的Socket,指定数据源端口号
		DatagramSocket ds = new DatagramSocket(10000);
		// 定义数据包,用于存储数据
		byte[] buf = new byte[1024];
		DatagramPacket dp = new DatagramPacket(buf, buf.length);
		// 通过服务的receive方法将收到的数据存入包中
		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::" + ip + " data::" + data + " port::" + port);
		// 关闭资源
		ds.close();
	}
}
运行结果:

4,TCP网络编程
        网络通信至少分为两个端点,即客户端和服务器端,由于TCP协议是面向连接的协议,必须要有一个通信实体主动接收来自其它通信实体的连接请求。
Java中能接收其它通信实体连接请求的类是ServerSocket,ServerSocket对象用于监听来自客户端的Socket连接,如果没有连接,它将一直处于等待状态。
1)ServerSocket
构造方法:
ServerSocket():创建非绑定服务器套接字。
ServerSocket(int port):创建绑定到特定端口的服务器套接字。
ServerSocket(int port, int backlog):利用指定的backlog 创建服务器套接字并将其绑定到指定的本地端口号。
ServerSocket(int port, int backlog, InetAddress bindAdr):使用指定的端口、侦听backlog和要绑定到的本地IP地址创建服务器。
常用方法:
Socket accept():侦听并接受到此套接字的连接。
void close():关闭此套接字。
InetAddress getInetAddress():返回此服务器套接字的本地地址。
int getLocalPort():返回此套接字在其上侦听的端口。
SocketAddress getLocalSocketAddress():返回此套接字绑定的端点的地址,如果尚未绑定则返回null。
boolean isBound():返回 ServerSocket 的绑定状态。
boolean isClosed():返回 ServerSocket 的关闭状态。

2)Socket
构造方法:
Socket(InetAddress address, int port):创建一个流套接字并将其连接到指定IP地址的指定端口号。
Socket(String host, int port):创建一个流套接字并将其连接到指定主机上的指定端口号。
Socket(InetAddress address, int port, InetAddress localAddr, int localPort):创建一个套接字并将其连接到指定远程地址上的指定远程端口。
Socket(String host, int port, InetAddress localAddr, int localPort):创建一个套接字并将其连接到指定远程主机上的指定远程端口。
常用方法:
void close():关闭此套接字。
InetAddress getInetAddress():返回套接字连接的地址。  
InetAddress getLocalAddress():获取套接字绑定的本地地址。
int getPort():返回此套接字连接到的远程端口。
int getLocalPort():返回此套接字绑定到的本地端口。
InputStream getInputStream():返回此套接字的输入流。
OutputStream getOutputStream():返回此套接字的输出流。
boolean isBound():返回套接字的绑定状态。
boolean isClosed():返回套接字的关闭状态。
void shutdownInput():此套接字的输入流置于“流的末尾”。
void shutdownOutput():禁用此套接字的输出流。
void setSoTimeout(int timeout):启用/禁用带有指定超时值的SO_TIMEOUT,以毫秒为单位。

3)演示示例
客户端
通过查阅Socket对象,发现在该对象建立时,就可以去连接指定的主机。
因为tcp是面向连接的,所以在建立Socket服务时,就要有服务端的存在,并连接成功,形成通路后,在该通道进行数据的传输。
需求:给服务端发送一个文本数据。
步骤:
1,创建Socket服务,并指定要连接的主机和端口。
2,建立流对象,获取Socket流中的输出流。
3,想流中写入数据.
4,关闭客户端。
5,代理服务器。

服务端
需求:定义断点接收数据并打印在控制台上。
步骤:
1,建立服务端的Socket服务,ServerSocket,并监听一个端口。
2,获取连接过来的客户端对象,通过ServerSocket的accept方法。这个方法是阻塞的。
3,客户端若发送来数据,服务端要使用对应的客户端对象,并获取到该客户端对象的读取流来读取发来的数据,并打印。
4,关闭服务端(可选)。

import java.net.*;
import java.io.*;
class TcpClient {
	public static void main(String[] args) throws Exception {
		// 创建客户端的socket服务,指定目的主机和端口
		Socket s = new Socket("192.168.1.7", 10003);
		// 为了发送数据,应该获取socket流中的输出流
		OutputStream out = s.getOutputStream();
		byte[] buf = "zhe shi yi duan shu ju".getBytes();
		out.write(buf, 0, buf.length);
		s.close();
	}
}

class TcpServer {
	public static void main(String[] args) throws Exception {
		// 建立服务端socket服务,并监听一个端口
		ServerSocket ss = new ServerSocket(10003);
		// 通过accept方法获取连接过来的客户端对象
		Socket s = ss.accept();
		// 通过Socket对象的getInetAddress方法获取客户端IP地址
		String ip = s.getInetAddress().getHostAddress();
		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(); // 关闭客户端对象的连接
		ss.close(); // 关闭服务端对象的连接(可选)
	}
}

运行结果:

4)加入多线程
        前面的Server和Client只是进行了简单的通信操作:Server端接收到Client端发来的数据打印出后,Server端和Client端一起关闭,而且这种模式只能进行单线程的数据传输,不能让多个客户端同时向服务端发送数据或者请求,为解决这个问题,必须得引进多线程方式来解决多客户端并发访问服务器。
         需求:多个客户端同时向服务端上传图片,并且图片的格式只能是.jpg格式。

import java.io.*;
import java.net.*;
class PicClient {
	public static void main(String[] args) throws Exception {
		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;
		}

		Socket s = new Socket("192.168.1.7", 10007);
		FileInputStream fis = new FileInputStream("1.jpg");
		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[] b = new byte[1024];
		int num = 0;
		while ((num = in.read(b)) != -1) {
			System.out.println(new String(b, 0, num));
		}
		fis.close();
		s.close();
	}
}
class PicThread implements Runnable {
	private Socket s = null;
	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("c:\\" + ip + "(" + count++ + ")" + ".jpg");
			while (file.exists())
				file = new File("c:\\" + 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());
			fos.close();
			s.close();
		} catch (Exception e) {
			throw new RuntimeException("上传失败");
		}
	}
}
class PicServer {
	public static void main(String[] args) throws Exception {
		ServerSocket ss = new ServerSocket(10007);
		while (true) {
			Socket s = ss.accept();
			new Thread(new PicThread(s)).start();
			new Thread(new PicThread(s)).start();
			new Thread(new PicThread(s)).start();
		}
	}
}
5)自定义服务器
    需求:这次自定义一个服务器,将客户端选择为本机上装有的IE浏览器,使用IE在其地址栏输入本地资源地址,访问到服务器发送来的数据,并解析到IE中显示出来。

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

class ServerDemo {
	public static void main(String[] args) throws Exception {
		ServerSocket ss = new ServerSocket(11000);
		Socket s = ss.accept();
		PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
		pw.write("<font size='7' color='red'>客户端你好</font>");
		pw.close();
		s.close();
	}
}
运行结果: 




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值