【黑马程序员】Java基础12:UDP和TCP的网络通讯

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


一、网络通讯概述

网络通讯三要素:

1IP地址:每台主机都有一个唯一的标识,即IP地址;每台计算机都有一个本地回环地址:127.0.0.1,主机名是localhost

2、端口:数据要发送到对方指定的应用程序上,为了标识这些应用程序,给这些网络应用程序都用数字进行标识,以便区分不同的进程,这个数字标识就叫做端口(有效端口是0---65535,其中0---1024是系统使用或保留的端口);

3、定义网络中数据传输的通讯规则(传输协议):TCP/IP

 

Socket:为网络服务提供的一种机制

1Socket就是两台机器间通信的端点,通信的两端都要有Socket

2、网络通信其实就是Socket间的通信,数据在两个Socket间通过IO传输;

3、传输方式有两种:UDP传输和TCP传输。

 

二、UDPTCP各自的特点

1、UDP:用户数据包协议

特点:

提供的是面向无连接的、不可靠的数据流传输;

数据会被封包,包体积限制在64K以内;                

UDP在传输数据包之前不用建立连接,所以传输速度很快;

因为无连接,所以并不知道数据包是否到达了目的地。

适用情况:        

当强调传输性能而不是传输的完整性时,如:音频和多媒体应用,UDP是最好的选择;

在数据传输时间很短,以至于此前的连接过程成为整个流量主体的情况下,UDP也是一个好的选择,如:DNS交换。

应用举例:

即时通讯、网络视频会议、桌面共享等。

 

2、TCP:传输控制协议

特点:

提供的是面向连接、可靠的数据流传输;

要通过三次握手完成连接,建立TCP连接通道之后才能传输数据;

TCP能够进行大数据的传输,有流量控制和差错控制,正因为有可靠性的保证和控制手段,所以传输效率比UDP低;

适用情况:

主要用于一次传输大量数据的情形,如:文件传输,远程登录等;

若要保证数据传输的完整性和可靠性时,TCP协议是最好的选择。

应用举例:

软件下载、收发邮件、远程登陆等。

 

三、UDPTCP传输示例

UDP传输:编写一个多线程聊天系统,一个当作发送端,一个当作接收端

package blog.itheima;

//编写一个多线程聊天系统,一个当作发送端,一个当作接收端
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

class MultiThreadUDPChat {
	public static void main(String[] args) throws Exception{
		//通过DatagramSocket对象创建udp服务;可不定义发送端口,发送时再分配
		DatagramSocket sendSocker = new DatagramSocket();
		//创建接收器的udp socket,建立端口
		DatagramSocket recSocker = new DatagramSocket(10000);
		//开启发送和接收二个线程
		new Thread(new Send(sendSocker)).start();
		new Thread(new Rec(recSocker)).start();
	}
}

class Send implements Runnable{
	private DatagramSocket ds;
	//构造函数,接收一个DatagramSocket对象,即sendSocker
	Send(DatagramSocket ds){
		this.ds = ds;
	}

	public void run() {
		try{
			BufferedReader bf = 
					new BufferedReader(new InputStreamReader(System.in));
			String line = null; 
			while ((line=bf.readLine())!=null){
				//把从键盘录入的一行数据存储进数组中
				byte[] buf = line.getBytes();
				/*确定数据,并封装成数据名,发送构造方法:DatagramPacket(
				byte[] buf, int length, InetAddress address, int port)*/ 
				DatagramPacket dp = new DatagramPacket(buf,buf.length,
						InetAddress.getByName("192.168.2.101"),10000);
				//数据封装好之后,发送出去
				ds.send(dp);
				//定义结束标记
				if ("886".equals(line))
					break;
			}
			//ds.close();若循环发送,可不用关闭
		}catch (Exception e){
			throw new RuntimeException("发送信息失败");
		}
	}
}

class Rec implements Runnable{
	private DatagramSocket ds;
	
	Rec(DatagramSocket ds){
		this.ds = ds;
	}

	public void run(){
		try
		{	//接收线程循环接收数据
			while(true){
				byte[] buf = new byte[1024];
				/*DatagramPacket(byte[] buf, int length) 
				构造 DatagramPacket,用来接收长度为 length 的数据包。*/
				DatagramPacket dp = new DatagramPacket(buf,buf.length);
				//阻塞式方法,没有数据就会等待
				ds.receive(dp);
				//dp.getAddress() 获取IP地址封装成的对象
				String ip = dp.getAddress().getHostAddress();
				//getLength() 返回将要发送或接收到的数据的长度。
				String data = new String(dp.getData(),0,dp.getLength());
				System.out.println(ip+"---"+data);

				if ("886".equals(data)){
					System.out.println(ip+"....离开聊天室");
					break;
				}
			}		
		}catch (Exception e){
			throw new RuntimeException("接收信息失败");
		}
	}
}

 

TCP传输:

1客户端从键盘输入读取一个字符串,发送到服务器

2服务器接收客户端发送的字符串,反转之后发回客户端客户端接收并打印。

package com.itheima;

/**
 * 使用TCP协议,客户端从键盘输入读取一个字符串,发送到服务器。
 * 服务器接收客户端发送的字符串,反转之后发回客户端。客户端接收并打印。
 */

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 TcpClient {
	
	public static void main(String[] args) {
		//建立客户端Socket通讯服务,初始值为null
		Socket socket = null;
		try {
			//指定Socket的IP地址和端口,以下IP是本机地址
			socket = new Socket("192.168.2.100",10000);
		} catch (Exception e) {
			System.out.println("服务器连接失败");
		}
		//键盘录入字符
		BufferedReader bufIn = 
				new BufferedReader(new InputStreamReader(System.in));
		
		PrintWriter out = null;			//定义初始化的字符输出流
		BufferedReader bufRead = null;	//定义初始化的缓冲读取流
		String line = null;				//定义初始化的键盘录入数据,每次读一行
		try {
			/*获取socket流中的输出流,用字符输出流写出,
			加上true以后,对println、printf 或 format 可以自动刷新*/
			out = new PrintWriter(socket.getOutputStream(),true);
			//获取socket流中的输入流,用缓冲流读取
			bufRead = 
			new BufferedReader(new InputStreamReader(socket.getInputStream()));
			
			while((line=bufIn.readLine())!=null){	//如果键盘录入的数据不为空,遍历循环				
				out.println(line);	//将键盘录入的数据发送到服务器
				//定义结束标记为over字符,放在这里可以将标记传到服务器,同时能够防止返回数据为null的情况
				if("over".equals(line))	
					break;
				System.out.println(bufRead.readLine());	//打印服务器传输回来的数据
			}
		} catch (IOException e) {
			System.out.println("客户端数据流传输失败");
		} finally {
			//键盘录入和流服务最后需要关闭,不能一直占用系统资源,所以放在finally中执行
			try {
				if(bufIn!=null)
					bufIn.close();
			} catch (IOException e) {
				System.out.println("键盘录入关闭失败");
			}
			try {
				if(socket!=null)
					socket.close();
			} catch (IOException e) {
				System.out.println("关闭服务器连接失败");
			}
		}
	}
}

class TcpServer{
	public static void main(String[] args) {
		//初始化服务器
		ServerSocket serverSocket = null;
		try {
			//创建服务器socket服务,并监听一个端口
			serverSocket = new ServerSocket(10000);
		} catch (IOException e) {
			System.out.println("服务器建立失败");
		}
		Socket socket = null;
		try {
			//通过accept方法获取连接过来的客户端对象,返回的是socket对象
			socket = serverSocket.accept();
		} catch (IOException e) {
			System.out.println("服务端连接客户端失败");
		}
		BufferedReader bufr = null;	//初始化缓冲读取流
		PrintWriter out = null;		//初始化字符输出流
		StringBuilder sb = null;	//初始化StringBuilder对象
		String line = null;
		
		//通过socket流,得到主机IP地址
		String ip = socket.getInetAddress().getHostAddress();
		System.out.println("IP:"+ ip +" 用户已连接");
		try {
			//获取socket流中的输入流,用缓冲流读取
			bufr = 
			new BufferedReader(new InputStreamReader(socket.getInputStream()));
			//获取socket流中的输出流,用字符输出流写出,自动刷新
			out = new PrintWriter(socket.getOutputStream(),true);
			
			while((line=bufr.readLine())!=null){		
				//如果客户端一直有数据传输过来,那么一直循环读取
				if("over".equals(line)){				
					//当收到客户端的over结束标记时,停止循环,不必再传输数据到客户端
					System.out.println("客户端退出连接,服务器关闭");
					break;	
				}
				sb = new StringBuilder(line);			
				//把客户端传输来的数据存入StringBuilder
				StringBuilder reverse = sb.reverse();	
				//利用StringBuilder的功能把字符串进行返转
				out.println(reverse);					
				//把反转后的数据传到客户端
				
				//在服务端打印出客户端输入的数据和反转后的数据
				System.out.println("客户端输入的数据为:"+line+";反转后的数据为:"+reverse);
			}
		} catch (IOException e) {
			System.out.println("服务器数据流传输失败");
		} finally {
			//数据流传输和服务器最后需要关闭,不能一直占用系统资源,所以放在finally中执行
			try {
				if(socket!=null)
					socket.close();
			} catch (IOException e) {
				System.out.println("关闭客户端连接失败");
			}
			try {
				if(serverSocket!=null)
					serverSocket.close();
			} catch (IOException e) {
				System.out.println("服务器关闭失败");
			}
		}
	}
}
---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值