黑马程序员_Java基础_网络编程

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

一、简介

Java的网络编程提供了两种通讯协议,UDP(数据报协议)与TCP(传输控制协议),网络通讯有三个要素:1、IP地址,2、端口号,3、网络协议

UDP与TCP的区别:

UDP将数据及源和目的封装成数据包,不需要建立链接,每个数据报的大小在现状在64k内,因无连接,所以是不可靠的协议,不需要建立连接,速度快。

TCP建立连接,形成传输数据的通道,在连接中进行大数据量传输,通过三次握手完成连接,是可靠协议,必须建立连接,效率会稍低。

二、UDP协议简单例子与知识点

在UDP协议中,一个通讯路径中有两个端点,一个是接收端,一个是发送端。

发送端:实例化UDP连接的类DatagramSocket(端口号(可选)),封装数据报包DatagramPacket,数据报包相当于邮件包,里面需要封装数据,数据的大小,发送的目的地(ip与端口),最后通过send方法发送数据

接收端:实例化UDP连接DatagramSocket(必填) 此时端口号应该明确说明,因为这样才能让发送端更方便的知道接收方的地址,在接受数据之前,需要准备接收数据的容器,也就是DatagramPacket数据报包,接收数据调用receive()方法接收数据,封装到DatagramPacket包中,调用数据报包的方法,获取发送端的信息与数据,有一个小的知识点在这里提下,就是发送端数据报包的IP地址最后一位为255的话,就能够实现广播的效果,例如:192.168.0.255在这个网段的用户如果存在端口号一致的服务,都能接受到数据的。

例子:从键盘上输入一段文字,通过UDP发送给接收端,接收端,接收到数据显示在控制台上

package com.itheima.net;

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

/**
 * 网络编程之UDP程序设计
 * 模拟网络通讯,发送端发送数据,接收端接收到数据并打印在控制台上
 */
public class UDPDemo {

	public static void main(String[] args) throws Exception{
		//启动程序
		new Thread(new UDPReceive()).start();
		new Thread(new UDPSend()).start();
	}
}

/**
 * UDP发送端,把从控制台上接收到的数据发送到接收端
 * 发送端实现Runnable接口
 * 
 */
class UDPSend implements Runnable{
	
	public void run(){
		send();
	}
	//发送数据的方法
	private void send(){
		try{
			//定义UDP发送端
			DatagramSocket socket = new DatagramSocket();
			//接收控制台输入的数据,因为我的控制台上UTF-8编码会出现乱码,这里我采用GBK编码
			BufferedReader mReader = new BufferedReader(new InputStreamReader(System.in,"GBK"));
			String line = null;
			while((line = mReader.readLine())!=null){
				byte[] buf = line.getBytes();
				//定义要发送的数据包
													  	
				DatagramPacket p = new DatagramPacket
						/*发送的数据  数据长度    发送到的地址                                     	发送到那个端口*/
						(buf, 0, buf.length, InetAddress.getByName("127.0.0.1"), 10000);
				
				socket.send(p);
				if(line.equals("bye")){//键盘接收数据不能够停止,所以需要定义一个终止符
					break;
				}
			}
			System.out.println("发送端结束...");
		}catch(Exception e){e.printStackTrace();}
	}
}
class UDPReceive implements Runnable{
	public void run(){
		try{
			//定义UDP接收端,接收端采用多线程,接收端口号为10000的所有数据
			DatagramSocket socket = new DatagramSocket(10000);
			while(true){
				String result = receive(socket);
				System.out.println(result);
				//加上一下代码是为了终止接收端代码...终止接收端是不合理的。
				if(result!=null && result.equals("bye")){
					System.out.println("接收端结束...");
					break;
				}
			}
		}catch(Exception e){e.printStackTrace();}
		
	}
	private String receive(DatagramSocket socket){
		try{
			//因为UDP接收数据的最大长度为64k,所以定义一个64K为长度的缓冲区
			byte[] buf = new byte[64*1024];
			//定义数据包,接收数据
			DatagramPacket p = new DatagramPacket(buf, buf.length);
			socket.receive(p);
			//获取数据包中的数据
			byte[] result = p.getData();
			//得到发送端发送的数据
			String data = new String(result,0,result.length);
			System.out.println(p.getAddress().getHostName()+p.getPort()+"说:"+data);
			if(data.trim().equals("bye")){
				return "bye";
			}
			return null;
		}catch(Exception e){e.printStackTrace();return null;}
	}
}
三、TCP协议与知识点

Java中使用Socket(套接字)完成TCP程序的开发,使用此类可以方便的建立可靠的、双向的、持续的、点对点的通讯连接

TCP里面有两个重要的类:Socket类与ServerSocket类,ServerSocket类主要用在服务端程序的开发上(服务端一般都是多线程的),用于接收客户端的连接请求,常用的方法:

构造方法:ServerSocket(int port),创建ServerSocket实例,用于监听指定端口

方法:

accept();等待客户端连接,是一个阻塞式方法

getInetAddress();得到IP

close();关闭连接

在服务端每次运行时都会使用accept()方法等待客户端的请求,当有客户端请求过来时,说返回一个Socket对象,该对象就是一个客户端对象,Socket对象常用的方法有:

构造方法:Socket(String host,int port),构造Socket对象,同时指定要连接的服务器主机与端口号

方法:

getInputStream()返回Socket对象的输入流

getOutputStream()返回Socket对象的输出流

close()关闭连接

分析学习下面最简单的TCP程序(这里先使用单线程)

服务端代码

package com.itheima.net;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 最简单的一个TCP程序,服务端
 * 客户端输入一个小写字符,服务端把大写返回
 */
public class TCPServerDemo1 {
	
	public static void main(String[] args) throws Exception{
		//建立服务端
		ServerSocket ss = new ServerSocket(10001);
		//等待客户端请求
		Socket s = ss.accept();
		//客户端请求到来,输出客户端信息  主机名
		System.out.println(s.getInetAddress().getHostName()+"连接到我的服务....");
		BufferedReader mReader = new BufferedReader(new InputStreamReader(s.getInputStream()));
		OutputStreamWriter writer = new OutputStreamWriter(s.getOutputStream());
		String line = null;
		while((line = mReader.readLine())!=null){
			//转换为大写发送出去
			System.out.println("服务端接收到了数据....");
			writer.write(line.toUpperCase());
			//写出换行符,表示改行已经结束
			writer.write(System.getProperty("line.separator"));
			writer.flush();
			System.out.println("服务端数据返回。");
		}
		//释放资源.....
		s.close();
		ss.close();
	}

}

客户端代码

package com.itheima.net;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

/**
 * TCP程序的客户端
 * @author Administrator
 *
 */
public class TCPClientDemo1 {

	public static void main(String[] args) throws Exception{
		//向指定主机,这里是本机的指定端口10001发送请求
		Socket s = new Socket("127.0.0.1", 10001);
		
		//获取键盘输入
		BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
		//获取网络输出流对象,向服务端发送消息
		OutputStreamWriter out = new OutputStreamWriter(s.getOutputStream());
		//获取网络输入流对象,获取服务器返回值
		BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
		String line = null;
		while((line = reader.readLine())!=null){
			System.out.println(line+"---");
			out.write(line);
			//写出换行符,表示改行已经结束
			//当然这里也可以使用BufferedWriter,里面有newLine()方法很好用
			//还有就有更简单的方法PrintWriter 构造方法里面有autoFlush这个选项。
			out.write(System.getProperty("line.separator"));
			out.flush();
			System.out.println("客户端发送出了数据");
			System.out.println("服务端返回的数据是:"+in.readLine());
			
		}
	}
}

下面使用多线程的方式完成服务端代码的编写,支持多线程并发访问

需求:完成图片上传,服务器端支持多个客户端并发上传图片

服务端代码:

package com.itheima.net;

import java.io.BufferedInputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * TCP程序服务端
 * 目标:支持多个客户端并发访问
 * 技术:Socket、多线程、IO
 */
public class TCPServerDemo2 {
	
	public static void main(String[] args) throws Exception{
		//定义服务端
		ServerSocket ss = new ServerSocket(10002);
		//每个客户端进入,都会开辟一个新的线程,处理
		while(true){
			//等待客户端连接
			Socket s = ss.accept();
			//开启线程
			new Thread(new Server(s)).start();
		}
	}
}
final class Server implements Runnable{
	private Socket s;//每个服务端线程,对应一个客户端
	public Server(Socket s){
		this.s = s;
	}
	@Override
	public void run() {
		try{
			System.out.println(s.getInetAddress().getHostName()+"连接到服务端....");
			//得到网络输入流,接收客户端数据
			BufferedInputStream in = new BufferedInputStream(s.getInputStream());
			//得到文件输出流
			PrintStream writer = new PrintStream(System.currentTimeMillis()+".jpg");
			byte[] buf = new byte[1024];
			int len = -1;
			while((len = in.read(buf))!=-1){
				writer.write(buf, 0, len);
			}
			writer.close();
			//得到网络输出流,通知客户端,完成了上传
			PrintWriter out = new PrintWriter(new OutputStreamWriter(s.getOutputStream()),true);
			out.println("上传成功!");
			//下面是释放资源
			s.close();
			writer.close();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

客户端代码:

package com.itheima.net;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.concurrent.TimeUnit;

//模拟客户端--第一个
public class TCPClientDemo2 {

	public static void main(String[] args) throws Exception{
		Socket s = new Socket("127.0.0.1", 10002);
		//获取网络输入流,接收服务端反馈信息
		BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
		//获取网络输出流
		PrintStream out = new PrintStream(new BufferedOutputStream(s.getOutputStream()));
		//获取读取文件流
		BufferedInputStream reader = new BufferedInputStream(new FileInputStream("a.jpg"));
		
		byte[] buf = new byte[1024];
		int len = -1;
		while((len = reader.read(buf))!=-1){
			//模拟大文件
			TimeUnit.SECONDS.sleep(1);
			out.write(buf,0,len);
			out.flush();//晕..我以为资源关闭会自动flush的..测试结果还需要手动flush
		}
		//结束标志
		s.shutdownOutput();
		reader.close();
		//服务端返回数据
		String line = in.readLine();
		System.out.println("服务端说:"+line);
		s.close();
	}
}



我在做测试的时候,复制一份客户端启动,模拟多个客户端并发请求服务端...


最后,还有一个知识点需要点出,就是HttpURLConnection,这个类的使用方法在JavaIO里面有个例子,就是模拟多线程下载文件。


总结:

在网络编程开发思路都是一样的,获取服务端连接,发送请求,得到服务端反馈信息。


---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值