Java之网络编程



一、概述


1、网络参考模型


2、网络通讯要素
◆IP地址:InetAddress
      网络中设备的标识
      不易记忆,可用主机名
      本地回环地址:127.0.0.1主机名:localhost
◆端口号
      用于标识进程的逻辑地址,不同进程的标识
      有效端口:0~65535,其中0~ 1024系统使用或保留端口。
◆传输协议
      通讯的规则

      常见协议:TCP,  UDP

通讯例子模型:


3、TCP和UDP
UDP
      将数据及源和目的封装成数据包中,不需要建立连接
      每个数据报的大小在限制在64k内
        因无连接,是不可靠协议
      不需要建立连接,速度快
TCP
      建立连接,形成传输数据的通道。
      在连接中进行大数据量传输
      通过三次握手完成连接,是可靠协议(你收到了吗?我收到了。哦,我知道你收到了
        必须建立连接,效率会稍低

.4、Socke

Socket就是为网络服务提供的一种机制。
通信的两端都有Socket。
网络通信其实就是Socket间的通信。
数据在两个Socket间通过io传输。


二、UDP传输


◆DatagramSocket与DatagramPacket
◆建立发送端,接收端。
◆建立数据包。
◆调用Socket的发送接收方法。
◆关闭Socket 。
◆发送端与接收端是两个独立的运行程序。

1、发送端
在发送端要在数据包对象中明确目的地 IP及端口。
DatagramSocket ds=new DatagramSocket();
byte[]by="hello,udp".getBytes();
DatagramPacket dp=new DatagramPacket(by,0,by.length)
InetAddress.getByName(“127.0.0.1”),10000);
ds·send(dp);
ds.close();

2、接收端
在接收端,要指定监听的端口。
    DatagramSocket ds=new DatagramSocket(10000);
    byte[]by=n ew byte[1024];
    Datag ram Packet dp=new DatagramPacket(by,by.length);
    ds.receive(dp);
    String str=new String(dp.getData(),0,dp.getLength());
    System .out.println(str+”——”+dp.g etAdd ress());
    ds.close();

下面用一个通讯小程序演示

发送端:

package internet;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UdpSend {
	public static void main(String[] args) throws IOException {
//		建立数据借口规则
		DatagramSocket sendsocket = new DatagramSocket();
//		创建键录入的读取流
		BufferedReader buf = new BufferedReader(
				new InputStreamReader(System.in));

		String line = null;
		while ((line = buf.readLine()) != null) {
//			如果输入over的话就结束
			if ("over".equals(line))
				break;
//			把得到的字符转换成字节,并且放入字节数组
			byte by[] = line.getBytes();
//			把数据打包入数据包对象,出入字节数据,和字节数据长度,和确定主机的 IP 地址。,和接收端的端口号
			DatagramPacket sentpacket = new DatagramPacket(by, by.length,
					InetAddress.getByName("192.168.0.255"), 7778);
//			开始发送
			sendsocket.send(sentpacket);
		}
//		关闭流
		buf.close();
//		关闭资源
		sendsocket.close();
	}
}
接收端:

package internet;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UdpSend {
	public static void main(String[] args) throws IOException {
//		建立数据借口规则
		DatagramSocket sendsocket = new DatagramSocket();
//		创建键录入的读取流
		BufferedReader buf = new BufferedReader(
				new InputStreamReader(System.in));

		String line = null;
		while ((line = buf.readLine()) != null) {
//			如果输入over的话就结束
			if ("over".equals(line))
				break;
//			把得到的字符转换成字节,并且放入字节数组
			byte by[] = line.getBytes();
//			把数据打包入数据包对象,出入字节数据,和字节数据长度,和确定主机的 IP 地址。,和接收端的端口号
			DatagramPacket sentpacket = new DatagramPacket(by, by.length,
					InetAddress.getByName("192.168.0.255"), 7778);
//			开始发送
			sendsocket.send(sentpacket);
		}
//		关闭流
		buf.close();

操作结果为:




三、TCP传输


1TCP分客户端和服务端。客户端对应的对象是Socket,服务端对应的对象是ServerSocket

2、方法:

        ◆创建客户端对象:

              Socket():创建空参数的客户端对象,一般用于服务端接收数据

             Socket(String host,int port),指定要接收的IP地址和端口号

        创建服务端对象:ServerSocket(int port):指定接收的客户端的端口

       Socket accept():监听并接收到此套接字的连接

        void shutdownInput():此套接字的输入流至于“流的末尾”

        void shutdownOutput():禁用此套接字的输出流

        InputStream getInputStream():返回此套接字的输入流,Socket对象调用

        OutputStream getOutputStream():返回套接字的输出流,Socket对象调用

3、基本思路

客户端:

       客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。

        连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通过getInputStream(),getOutputStream()获取即可。

        与服务端通讯结束后,关闭Socket

服务端:

        服务端需要明确它要处理的数据是从哪个端口进入的。

        当有客户端访问时,要明确是哪个客户端,可通过accept()获取已连接的客户端对象,并通过该对象与客户端通过IO流进行数据传输。

        当该客户端访问结束,关闭该客户端。

4、步骤

客户端:

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

         创建Socket服务,并指定要连接的主机端口。通路一建立,就会产生Socket流(包括输入流和输出流),通过方法获取

        为了发送数据,应获取Socket中的输出流,如果要接收服务端的反馈信息,还需要获取Socket的输入流

        通过输出流的write()方法将要发送的数据写入到流中

        关闭Socket流资源

服务端:

        服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。需监听一个端口。

         建立服务端的Socket服务,并监听一个端口。通过ServerSocet带端口参数的构造函数

        获取连接过来的客户对象,通过ServerSocketaccept()方法,此方法是阻塞式的,如果服务端没有连接到就会一直等待

        客户端如果发过来数据,则服务端要使用对应的客户端对象,并获取到该客户端对象的读取流读取发过来的数据,并输出到指定目的地。

        关闭服务端(可选)。一般服务端是常开的,因为在实际应用中,随时有客户端在请求连接和服务。但这里需要定时关闭客户端对象流,避免某一个客户端长时间占用服务器端。

下面通过一个客服端与服务端连接与反馈来说明:

示例1:需求是客服端给服务端一堆小写字母,服务端返回其大写字母

先建立服务端:

package internet;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpTransSever {
	public static void main(String[] args) throws IOException {
//		建立服务端接口规则对象,和设定监视端口
		ServerSocket ssocket = new ServerSocket(18989);
//		获取到客服端的Socket对象
		Socket socket=ssocket.accept();
//		通过Socket对象获得客服端的输入流,并且放入缓冲区
		BufferedReader bur = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//		建立打印流,开始自动刷流
		PrintWriter pw = new PrintWriter(socket.getOutputStream(),true);
		String line = null;
//		还是读取
		while((line=bur.readLine())!=null)
		{	
//			先生对方的数据
			System.out.println(line);
//			用打印流把对应的大写字母反馈给客服端
			pw.println(line.toUpperCase());
			
		}
//		关流
		bur.close();
//		关闭资源
		ssocket.close();
	}
}

再建立客服端:

package internet;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class TcpTrans {
public static void main(String[] args) throws IOException {
//	建立规则借口对象,制定ip与端口号
	Socket socke = new Socket("192.168.0.101",18989);
//	建立键盘录入
	BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
//	建立输入流接受服务端的反馈
	BufferedReader buf = new BufferedReader(new InputStreamReader(socke.getInputStream()));
//	建议打印流把输入的数据给服务端
	PrintWriter  pw=new PrintWriter(socke.getOutputStream(),true);
	
	String line = null;
	while((line=bf.readLine())!=null)
	{
//		当输入over表示请求的服务结束
		if(line.equals("over"))
			break;
		pw.println(line);
//		读取反馈信息
		System.out.println(buf.readLine());
	}
	socke.close();
	bf.close();

}
}


结果为:

示例2:

需求是上传图片到服务端,当上传成功后服务端给与客服端反馈信息

建立服务端:

package internet;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ClientUploadHandle {
	public static void main(String[] args) throws IOException {
//		建立服务端口
		ServerSocket ssocket = new ServerSocket(22346);
//		获取客服端对象
		Socket socket = ssocket.accept();
//		通过客服端对象获取客服端输入流
		InputStream input = socket.getInputStream();
//		通过客服端对象获取输出流
		OutputStream output =socket.getOutputStream();
//		建立输出流
		FileOutputStream fos = new FileOutputStream("c://3.jpg");
//		建立缓存容器用来接受客服端的数据
		byte by[] =new byte[1024*10];
		int num=0;
		while((num=input.read(by))!=-1)
		{
//			数据加载到输出流
			fos.write(by, 0,num);
		}
//		数据加载完毕,提示用户
		output.write("恭喜你上传成功".getBytes());
//		关闭流,关闭资源
		socket.close();
		ssocket.close();
		fos.close();
	}
}
◆建立客服端:

package internet;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class ClientUpload {
	public static void main(String[] args) throws UnknownHostException, IOException {
//		建立客服端对象
		Socket socket = new Socket("192.168.0.101",22346);
//		把路径文件封装成对象
		File file = new File("c://1.jpg");
//		创建输入流
		FileInputStream fis = new FileInputStream(file);
//		利用对象获取输出流
		OutputStream sOut = socket.getOutputStream();
//		定义缓存容器
		byte by[]=new  byte[1024*10];
		int num=0;
		while((num=fis.read(by))!=-1)
		{
			sOut.write(by,0,num);
		}
//		结束输出,不然客服端循环里的read将会一直等待
		socket.shutdownOutput();
//		这个容器是接受服务端的反馈
		byte buf[]=new byte[1024];
//		建立输入流
		InputStream input = socket.getInputStream();
		int i =input.read(buf);
//		打印反馈信息
		System.out.println(new String(buf,0,i));
//		关闭流
		socket.close();
		fis.close();
		
	}
}
上传成功,客服端收到了反馈信息:



示例三:

这是一个验证客户端输入的用户名是否注册过,并且给出相应处理的小程序

建立服务端:

package internet;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/*
 服务端,判断客服端输入的用户名存不存在,如果有就返回恭喜你登录成功
 没有的话,提示从新输入,如果输入次数大于五次就断开服务
 这里需要考虑到多个客服端请求的情况
  */
public class TCPLoginServer {
	public static void main(String[] args) throws Exception {
//		建立黑名单的对象引用
		String Blacklist=null;
//		建立服务端对象,指定窗口
		ServerSocket server = new ServerSocket(8848);
//		获得客户端对象
		Socket socket = server.accept();
//		判断客户端的ip时候是黑名单中的数据
		String ip=socket.getInetAddress().getCanonicalHostName();
//		判断客户端的ip时候是黑名单中的数据,这里先不考虑并发和多个用户等情况,实际情况可用集合或者文本储存
		if(Blacklist==ip)
		{
			socket.close();
		}
		
//		获得客户端输入流
		BufferedReader bfr = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//		获得客户端输出流
		PrintWriter pw = new PrintWriter(socket.getOutputStream(),true);
//		建立读取存档的输入流指向
		BufferedReader bfr_2 =null;
		String line=null;
		String line_2=null;
//		建立标记
		boolean flag=false;
//		建立计数器
		int mark=0;
//		开始获得客服端传来的数据
		while((line=bfr.readLine())!=null)
		{
//			记录运行次数
			mark++;
//			判断运行次数
			if(mark>=5)
			{
//				反馈客服端
				pw.println("您尝试登录的次数已经到了");
//				把此客户端ip加入黑名单
				Blacklist=socket.getInetAddress().getCanonicalHostName();
//				关闭连接
				socket.close();
//				跳出循环
				break;
			}
//			把本地数据库加载到输入流
			bfr_2 = new BufferedReader(new FileReader("c:\\SQL.txt"));
//			读取数据
			while((line_2=bfr_2.readLine())!=null)
			{
//				查找数据库里是否有用户输入的用户名
				if(line.equals(line_2))
				{
//					有的话就反馈客户端登陆成功
					pw.println("用户:"+line+"您好!"+"恭喜您已经成功登录了");
//					改掉标记
					flag=true;
//					并且跳出循环
						break;
				}
			}
			if(!flag)
			{
				pw.println("没有这个用户,请重新输入");
			}
			
		}
//		关闭流
		bfr_2.close();
	}
}

◆建立客户端

package internet;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
/*
 客户端:输入自己的用户名登录,输入正确与错误都会有提示。超过五次错误本客户端ip将会被加入黑名单
 */
public class TCPLogin {

	public static void main(String[] args) throws Exception, Exception {
//		建立客户端对象
		Socket socket = new Socket("192.168.0.101",8848);
//		通过对象获得客户端的输入流
		 BufferedReader bfr=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//		 获得客户端对象的输出流
		PrintWriter pw=new PrintWriter(socket.getOutputStream(),true);
//		键盘录入
		BufferedReader burd =new BufferedReader( new InputStreamReader(System.in));
		String line = null;
//		读取键盘录入的数据
		while((line=burd.readLine())!=null)
		{
//			数据加载到打印流
			pw.println(line);
//			获得服务端反馈
			String str=bfr.readLine();
//			打印服务端反馈
			System.out.println(str);
//			反馈如果保护”成功“那么登陆成功,跳出循环
			if(str.contains("成功"))
				break;
		}
//		关闭资源
		socket.close();
	}
		
}
结果演示

当输入错误时:


当输入正确时;


当数次次数到达时,会被加入黑名单,并且断开连接


5、TCP客户端高并发的解决方案

上述程序演示的全是一对一的传输,但在实际开发中最常见的是客户端并发的访问服务端,下面我将对上面程序的代码做出修改

以满足并发请求的需

这是示例三的服务端多并发处理方案:

package internet;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/*
 服务端,判断客服端输入的用户名存不存在,如果有就返回恭喜你登录成功
 没有的话,提示从新输入,如果输入次数大于五次就断开服务
 这里需要考虑到多个客服端请求的情况
  */
public class TCPLoginServer {
	public static void main(String[] args) throws Exception {
//		建立黑名单的对象引用
		String Blacklist=null;
//		建立服务端对象,指定窗口
		ServerSocket server = new ServerSocket(8848);
//		获得客户端对象
		Socket socket = server.accept();
//		判断客户端的ip时候是黑名单中的数据
		String ip=socket.getInetAddress().getCanonicalHostName();
//		判断客户端的ip时候是黑名单中的数据,这里先不考虑并发和多个用户等情况,实际情况可用集合或者文本储存
		if(Blacklist==ip)
		{
			socket.close();
		}
		
//		获得客户端输入流
		BufferedReader bfr = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//		获得客户端输出流
		PrintWriter pw = new PrintWriter(socket.getOutputStream(),true);
//		建立读取存档的输入流指向
		BufferedReader bfr_2 =null;
		String line=null;
		String line_2=null;
//		建立标记
		boolean flag=false;
//		建立计数器
		int mark=0;
//		开始获得客服端传来的数据
		while((line=bfr.readLine())!=null)
		{
//			记录运行次数
			mark++;
//			判断运行次数
			if(mark>=5)
			{
//				反馈客服端
				pw.println("您尝试登录的次数已经到了");
//				把此客户端ip加入黑名单
				Blacklist=socket.getInetAddress().getCanonicalHostName();
//				关闭连接
				socket.close();
//				跳出循环
				break;
			}
//			把本地数据库加载到输入流
			bfr_2 = new BufferedReader(new FileReader("c:\\SQL.txt"));
//			读取数据
			while((line_2=bfr_2.readLine())!=null)
			{
//				查找数据库里是否有用户输入的用户名
				if(line.equals(line_2))
				{
//					有的话就反馈客户端登陆成功
					pw.println("用户:"+line+"您好!"+"恭喜您已经成功登录了");
//					改掉标记
					flag=true;
//					并且跳出循环
						break;
				}
			}
			if(!flag)
			{
				pw.println("没有这个用户,请重新输入");
			}
			
		}
//		关闭流
		bfr_2.close();
	}
}
◆下面是并发上传图片是服务端代码示例:

package internet;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ConcurrentUploadServer {
		public static void main(String[] args) throws IOException {
//			建立服务端对象
			ServerSocket ssocket = new ServerSocket(22346);
//			一有客服端请求就获取其客服端对象,并且为其单独开启一个线程
			while(true)
			{
				Socket socket = ssocket.accept();
				new Thread(new ConcurrentHandle(socket)).start();;
			}
			
		}
	}

class ConcurrentHandle implements Runnable
{
	private Socket socket;
	ConcurrentHandle(Socket socket)
	{
		this.socket =socket;
	}
	
	public void run()
	{	
		 InputStream input=null;
		 OutputStream output=null;
		 FileOutputStream fos=null;
//		 建立一个计数器
		 int temp =1;
		 String ip=socket.getInetAddress().getHostAddress();
		try
		{
//			得到客服端的输入输出流
			 input = socket.getInputStream();
			 output =socket.getOutputStream();
//			 把ip当作名字并且封装成对象
			 File file = new File("c:/",ip+".jpg");
//			 如果有这个文件的话,名字就为计计数器的值加上ip
			 while(file.exists())
			 {
				 file = new File("c:/",ip+"("+(temp++)+")"+".jpg");
			 }
//				 把数据加载到输出流
			 fos = new FileOutputStream(file);
		
//		建立缓冲区
		byte by[] =new byte[1024*10];
		int num=0;
			while((num=input.read(by))!=-1)
			{
//				加载到输入流
				fos.write(by, 0,num);
			}
//			反馈客服端上传成功
			output.write("恭喜你上传成功".getBytes());
		
			fos.close();
			socket.close();
		
			
		}
		catch(Exception e)
		{
			throw new RuntimeException("上传失败");
		}
		
	}
}

四、URLURLConnection

1URL

        URI:范围更大,条形码也包含于此范围

        URL:范围较小,即域名

方法:

        1)构造函数:URL(String protocol,String host,int port,String file);//根据指定 protocolhostport号和 file 创建 URL对象。

        2String getProtocol();//获取协议名称

        3String getHost();//获取主机名

        4int getPort();//获取端口号

        5String getFile();//获取URL文件名

        6String getPath();//获取此URL的路径部分

        7String getQuery();//获取此URL的查询部,客户端传输的特定信息

注:一般输入网址,是不带端口号的,此时可进行获取,通过获取网址返回的port,若port-1,则分配一个默认的80端口,如

        int port = getPort();

        if(port == -1)

              port = 80;

2URLConnection

方法:

        1URLConnection openConnection();//URL调用此方法,返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。

        2InputStream getInputStream();//获取输入流

        3OutputStream getOutputStream();//获取输出流



感谢浏览,不足之处希望谅解




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值