黑马程序员-java学习笔记-网络编程

---------------------- android培训java培训、期待与您交流! ----------------------

Java语言是在网络环境下诞生的,所以Java语言虽然不能说是对于网络编程的支持最好的语言,但是必须说是一种对于网络编程提供良好支持的语言,使用Java语言进行网络编程将是一件比较轻松的工作。

         和网络编程有关的基本API位于java.net包中,该包中包含了基本的网络编程实现,该包是网络编程的基础。该包中既包含基础的网络编程类,也包含封装后的专门处理WEB相关的处理类。

TCP/IP协议

     包含了一系列构成互联网基础的网络协议。这些协议最早发源于美国国防部的ARPA网项目。TCP/IP模型也被称作DoD模型(Department of Defense Model)。TCP/IP字面上代表了两个协议:TCP(传输控制协议)和IP(网际协议)。


    所谓协议,就是约定的规则或者格式,就像写信一样。信中有格式,比如定格写对方姓名,然后就是正文,最后写上自己名字和写信日期。写完信后,需要用规定的信封封好,然后再右上角贴上邮票,写上邮政编码、写上收件人地址和寄信人地址。这样统一的格式规定后,就方便与大家一起通信了。

   TCP/IP协议将网络通信运用了分层思想,将协议分成五层。

   应用层就是我们接触最多的层,我们浏览网页,发送邮件,QQ聊天,都是在这个层。拿QQ聊天举例,我们向好友发送消息,首先找到好友的IP地址,TCP/IP协议会按照规则,将消息一层一层的进行包装,最后由网络物理层,也就是网线等传输介质传送到该好友的主机,该主机接收到后,会一层一层的将接收到的数据包进行解包,解包也是按照规则,一层层褪去外面包裹的层,最后解包到应用层之后,得到了消息,但是怎么知道是QQ发出的消息,而是飞秋发出的消息呢(假设主机上运行多个不同的通讯工具)?原来每个程序在操作系统中都用数字进行了标识,这个数字在0~65535之间,称为端口。这个端口是逻辑端口,不是实际中主机后面那些实在的端口。所以只要找到相同编号的端口,程序就可以正确接收数据了。

  综合上面的说法,我们总结出网络编程需要涉及到的3个关键点

1)ip地址2)端口3)协议。

我们先从IP地址说起

InetAddress类。该类的功能是代表一个IP地址,并且将IP地址和域名相关的操作方法包含在该类的内部。该类没有构造函数,那么我们自然想到找本类中静态方法获取对象了。

InetAddress i=InetAddress.getLocalHost();//获取本地主机IP地址
i.toString();//获取主机名+地址
i.getAddress();//获取地址
i.getHostName();//获取主机名
再来看看下面代码

InetAddress i1=InetAddress.getByName("www.baidu.com");
i1.getHostName();
InetAddress[] i2=InetAddress.getAllByName("www.baidu.com");

因为百度有多台服务器主机,访问的时候不一定都是访问同样的主机,所以i1不一定得到同样的主机名,getAllName()方法可以返回所有百度ip地址。
网络编程中有UDP编程和TCP编程两种方式,好比发短信和打电话这两种方式的通信,使用拨打电话的方式可以保证将信息传递给 别人,因为别人接听电话时本身就确认接收到了该信息。而发送短信的方式价格低廉,使用方便,但是接收人有可能接收不到。

UDP编程

1)无连接发送,是不可靠的协议

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

3)每个数据包的大小限制在64K之内(数据发送都需要封装,封装的时候就需要规定了大小)

TCP编程

1)建立连接,形成传输数据的通道,好比打电话两个人必须保持连线

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

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

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

Socket

   前面提到网络编程的3个关键点,IP地址,端口,协议。TCP、UDP属于传输协议,那IP地址和端口就有Socket来表示了。socket的英文原义是“孔”或“插座”。作为4BDS UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原意那样,象一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务。

  通信两端都有Socket,网络通信就是Socket间的通信。

UDP传输方式编程

这里涉及两个类,一个是DatagramSocket,这是接收和发送数据包的Socket;一个是DatagramPacket,这是Udp数据包,该类中带参数(参数是地址)的构造方法一般为发送端。

首先我们来编写一个简单发送文字的小程序,这里用udp方式传输

创建UDP传输的发送端。
思路:
1)建立udp的socket服务。
2)将要发送的数据封装到数据包中。 
3)通过udp的socket服务将数据包发送出去。
4)关闭socket服务。

      //1,udpsocket服务。使用DatagramSocket对象。
		DatagramSocket ds = new DatagramSocket(8888);
		
		//2,将要发送的数据封装到数据包中。
		String str = "udp传输演示:哥们来了!";
			//使用DatagramPacket将数据封装到的该对象包中。
		byte[] buf = str.getBytes();
		
		DatagramPacket dp = 
				new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.100"),10000);
		
		
		//3,通过udp的socket服务将数据包发送出去。使用send方法。
		ds.send(dp);
		
		//4,关闭资源。
		ds.close();

 建立UDP接收端的思路。
1)建立udp socket服务,因为是要接收数据,必须要明确一个端口号。
2)创建数据包,用于存储接收到的数据。方便用数据包对象的方法解析这些数据.
3)使用socket服务的receive方法将接收的数据存储到数据包中。
4)通过数据包的方法解析数据包中的数据。
5)关闭资源 

         //1,建立udp socket服务。
		DatagramSocket ds = new DatagramSocket(10000);
		
		
		//2,创建数据包。
		byte[] buf = new byte[1024];
		DatagramPacket dp = new DatagramPacket(buf,buf.length);
		
		//3,使用接收方法将数据存储到数据包中。
		ds.receive(dp);//阻塞式的。
		
		//4,通过数据包对象的方法,解析其中的数据,比如,地址,端口,数据内容。
		String ip = dp.getAddress().getHostAddress();
		int port = dp.getPort();
		String text = new String(dp.getData(),0,dp.getLength());
		
		System.out.println(ip+":"+port+":"+text);
		
		//5,关闭资源。
		ds.close();

接下来我进一步编写一个UDP聊天的小程序

分析:1)聊天有接收数据和发送数据两部分,两部分同时运行,这时就需要多线程技术

2)一个线程用于控制发送,一个线程用于控制接收,因为接收和发送动作不一致,所以需要定义两个run方法

发送端编写

public class Send implements Runnable {

	private DatagramSocket ds;
	
	public Send(DatagramSocket ds){
		this.ds = ds;
	}
	
	@Override
	public void run() {
		
		try {
			BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
			String line = null;
			
			while((line=bufr.readLine())!=null){
				
				
				byte[] buf = line.getBytes();
				DatagramPacket dp = 
						new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.255"),10001);
				ds.send(dp);
				
				if("886".equals(line))
					break;
			}
			
			ds.close();
		} catch (Exception e) {
		}
	}

}

接收端编写

public class Rece implements Runnable {

	private DatagramSocket ds;

	public Rece(DatagramSocket ds) {
		this.ds = ds;
	}

	@Override
	public void run() {
		try {
			while (true) {

				// 2,创建数据包。
				byte[] buf = new byte[1024];
				DatagramPacket dp = new DatagramPacket(buf, buf.length);

				// 3,使用接收方法将数据存储到数据包中。
				ds.receive(dp);// 阻塞式的。

				// 4,通过数据包对象的方法,解析其中的数据,比如,地址,端口,数据内容。
				String ip = dp.getAddress().getHostAddress();
				int port = dp.getPort();
				String text = new String(dp.getData(), 0, dp.getLength());
				
				System.out.println(ip + "::" + text);
				if(text.equals("886")){
					System.out.println(ip+"....退出聊天室");
				}

			}
		} catch (Exception e) {

		}

	}

}

最后进行调用

public static void main(String[] args) throws IOException {

		
		DatagramSocket send = new DatagramSocket();
		
		DatagramSocket rece = new DatagramSocket(10001);
		new Thread(new Send(send)).start();
		new Thread(new Rece(rece)).start();
		
	}
TCP传输方式编程

TCP也涉及两个类,一个Socket,对应客户端,一个ServerSocket,对应服务端。通过查阅Socket对象,发现在该对象建立时就可以去连接指定主机,因为TCP是面向连接的,所以在建立Socket服务时就要服务端存在,并连接成功,形成通路后在该通道进行数据的传输

客户端发数据到服务端

 Tcp传输,客户端建立的过程。
1)创建tcp客户端socket服务。使用的是Socket对象。建议该对象一创建就明确目的地。要连接的主机。 
2)如果连接建立成功,说明数据传输通道已建立。
该通道就是socket流 ,是底层建立好的。 既然是流,说明这里既有输入,又有输出。
想要输入或者输出流对象,可以找Socket来获取。 
可以通过getOutputStream(),和getInputStream()来获取两个字节流。
3)使用输出流,将数据写出。 
4)关闭资源。
 

//创建客户端的Socket服务时指定目的主机和端口
Socket socket = new Socket("192.168.1.100",10002);		
//为了发送数据,应该获取Socket中的输出流		
OutputStream out = socket.getOutputStream();	
				
out.write("tcp演示:哥们又来了!".getBytes());		
		
//读取服务端返回的数据,使用socket读取流。 
InputStream in = socket.getInputStream();
byte[] buf = new byte[1024];		
int len = in.read(buf);		
String  text = new String(buf,0,len);
//打印数据		
System.out.println(text);		
//关闭资源。
socket.close();



服务端接收客户端发送过来的数据,并打印在控制台上。 

建立tcp服务端的思路:
1)创建服务端socket服务。通过ServerSocket对象。
2)服务端必须对外提供一个端口,否则客户端无法连接。
3)获取连接过来的客户端对象。
4)通过客户端对象获取socket流读取客户端发来的数据 ,并打印在控制台上。

5)关闭资源。关客户端,关服务端

//1创建服务端对象。
		ServerSocket ss = new ServerSocket(10002);
		
		//2,获取连接过来的客户端对象。
		Socket s = ss.accept();
		
		String ip = s.getInetAddress().getHostAddress();
		
		//3,通过socket对象获取输入流,要读取客户端发来的数据
		InputStream in = s.getInputStream();
		
		byte[] buf = new byte[1024];
		
		int len = in.read(buf);
		String text = new String(buf,0,len);
		System.out.println(ip+":"+text);//打印到控制台
		
		
		
		//使用客户端socket对象的输出流给客户端返回数据
		OutputStream out = s.getOutputStream();
		out.write("收到".getBytes());
		
		s.close();
		ss.close();

利用TCP上传图片

客户端编写

//建立socket
		Socket s=new Socket("127.0.0.1",10007);

		FileInputStream fis=new FileInputStream("demo.bmp");
		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);
		new String(bufIn,0,num);
		//关闭流资源
		fis.close();
		s.close();

服务端的编写

ServerSocket ss=new ServerSocket(10007);
		while(true){
			//监听客户端
			Socket s=ss.accept();
			InputStream in=s.getInputStream();
			//建立文件流
			FileOutputStream fos=new FileOutputStream("server.bmp");
			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();
		}

accept()方法是阻塞式的,所以加入while(true)也不会死循环。

顺利上传了图片,但是该服务器一次只能服务一个客户端。当A客户端连上服务器之后被服务端获取到。服务端执行具体流程,这时B客户端连接进来,只能等待A客户端处理完。

这样的服务器不实用,服务器应该可以并发的接受客户端上传的图片。

客户端并发上传图片

将每个客户端封装到一个单独的线程中就可以同时处理多个客户端的请求了。

客户端的代码不变。

服务端代码的编写

public class imageMutServer implements Runnable{
	private Socket s;//封装一个客户端
	public imageMutServer(Socket s) {
		this.s=s;
	}
	public void run(){
		int count=1;
		String ip=s.getInetAddress().getHostAddress();
		try {
			InputStream in=s.getInputStream();
			File file=new File(ip+"("+count+")"+".jpg");
			//如果文件已经存在
			while(file.exists()){
				//重命名
				file=new File(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("上传失败");
		}
	}

	public static void main(String[] args) throws Exception {
		ServerSocket ss=new ServerSocket(10007);
		while(true){
			Socket s=ss.accept();
			new Thread(new imageMutServer(s)).start();
		}

	}

}








---------------------- android培训java培训、期待与您交流! ----------------------

详细请查看:http://edu.csdn.net

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值