Java中的TCP编程与UDP编程简介

网络编程的常用协议

        IP协议是个分组交换协议,它不保证可靠传输。而TCP协议 是传输控制协议,它是面向连接 的协议,支持可靠传输建立协议之上的,简单地说,IP协议只负责发数据包,不保证顺序和正确性,而TCP协议负责控制数据包传输,它在传输数据之前需要先建立连接,建立连接后才能传输数据,传输完后还需要断开连接。TCP协议之所以能保证数据的可靠传输,是通过 接收确认 、 超时重传 这些机制实现的。并且,TCP协议允许双向通信,即通信双方可以同时发送和接收数据。   

        TCP协议也是应用最广泛的协议,许多高级协议都是建立在TCP协议之上的,例如HTTPSMTP等。
        UDP协议(User Datagram Protocol)是一种数据报文协议,它是无连接协议,不保证可靠传输。因为UDP协议在通信前不需要建立连接,因此它的传输效率比TCP高,而且UDP协议,TCP 协议要简单得多。选择UDP协议时,传输的数据通常是能容忍丢失的,例如,一些语音视频通信的应用会选择UDP 协议。

什么是Socket?

        我们要了解什么是TCP编程与UDP编程,首先我们需要先了解什么是Socket。

        Socket是一个抽象的概念,一个应用程序通过Socket建立一个远程连接,而Socket内部通过TCP/IP协议把数据传送至网络。

        Socket就是套接字,由IP地址和端口号(范围是0~65535)组成,端口号是由操作系统随机分配的,它是一个0~65535之间的一个数字。小于1024的端口号是特权端口,一般都是需要管理员权限,但是大于1024的端口号可以被任意用户的应用程序打开。通过端口号,Socket才能正确连接本机中的应用程序。

        当使用Socket进行网络编程时,本质上就是两个进程之间的网络通信。其中一个进程必须充当服务器端,它会主动监听某个指定的端口,另一个进程必须充当客户端,它必须主动连接服务器的IP地址和指定端口,如果连接成功,服务器端和客户端就成功地建立了一个TCP连接,双方后续就可以随时发送和接收。

        因此,当Socket 连接成功地在服务器端和客户端之间建立后:

        对服务器端来说,它的Socket是指定的IP地址和指定的端口号。

        对客户端来说,它的Socket是它所在计算机的IP地址和一个由操作系统分配的随机端口号。

TCP编程 (实现人机对话)

服务器端

        首先我们先将需要和人机对话的内容存入一个Map集合用来存储问题与回答,当客户端发出问题时,如果有则返回相对应的值,如果没有,则返回null。

Map<String, String> chatMap = new HashMap<String, String>(){
			{
				put("你好", "你好呀");
				put("hi", "hi~");
				put("hello", "哈喽");
				put("吃了吗", "没呢,你呢");
				put("孤勇者", "爱你孤身走暗巷");
				put("有请潘周聃", "潘周聃,今年29岁,苏黎世理工大学.....");
				put("很高兴认识你", "我也是哦");
			}
		};

        然后,我们创建一个Socket对象,在服务器端通过Java通过的ServerSocket创建,传入一个监听端口,通过一个死循环使得服务器一直处于监听状态,使用accept()方法接收来自客户端的连接。接着创建一个输入流,并调用getInputStream()方法,获取来自客户端提出的问题。再创建一个输出流,将问题的回答反馈给客户端。

具体代码实现如下:

public class ChatServer {
	public static void main(String[] args) {
		Map<String, String> chatMap = new HashMap<String, String>(){
			{
				put("你好", "你好呀");
				put("hi", "hi~");
				put("hello", "哈喽");
				put("吃了吗", "没呢,你呢");
				put("孤勇者", "爱你孤身走暗巷");
				put("有请潘周聃", "潘周聃,今年29岁,苏黎世理工大学.....");
				put("很高兴认识你", "我也是哦");
			}
		};

		try(ServerSocket server = new ServerSocket(9966)){
			
			while(true) {
				// 发生客户端连接
				Socket client = server.accept();
				
				// 获取该客户端的IP地址
				String clientIP = client.getInetAddress().getHostAddress();
				
				try(BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
					BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()))){
					
					// 获取该客户端的提问
					String question = reader.readLine();
					System.out.println("【服务器】 来自客户端" + clientIP + "提问:" + question);
					
					// 获取该问题的答案
					String answer = chatMap.get(question);
					answer = answer == null ? "我不知道你在说什么!" : answer;
			
					// 发送答案至客户端
					writer.write(answer);
					writer.flush();
				}
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

        我们可以用一个简单的三元运算符来判定如果返回值为null时,可以输出指定的内容。

客户端

        客户端相比于服务器端比较简单。

        首先创建Socket对象,并传入服务器端的IP地址和端口号,通过Scanner获取来自用户提出的问题。然后创建输出流,通过getOutputStream()将问题传输给服务器端,再创建输入流用来获取来自服务器端的回答。

public class CharClient {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		
		while(true) {
			// 创建Socket,连接服务器
			try (Socket client = new Socket("192.168.254.153", 9966);
					BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
					BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
				){
				// 获取控制台的输入(问题)
				String question = input.nextLine();
				if(question.equals("over")) {
					break; // 结束循环
				}
				
				// 发送问题至服务器
				writer.write(question);
				writer.flush();
				
				// 暂时结束本次输出
				client.shutdownOutput();
				
				// 获取来自服务器的“答案”
				String answer = reader.readLine();
				System.out.println("【客户端】 来自服务器的回答:" + answer);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		System.out.println("本次问答结束!");
	}
}

运行结果显示:

 UDP编程(简单的双人对话实现)

        相对于TCP来说,UDP就简单很多,UDP不需要建立连接,也没有通过数据流传送,而是通过一个一个的数据包来实现。

        UDP也是需要使用Socket来监听端口的的,不过它使用的是java提供的DatagramSocket。

发送端

        发送数据时:通过setData()方法,将要发送的内容转换成字符串存储到定义的空数组中,再调用send()方法,实现数据的发送。

        接收数据时:通过receive()方法,将接收到的内容传入之前receivePacket包定义的空数组,通过DatagramPacket返回的getData()、getOffset()和getLength()获取数据,并转成字符串,实现数据的接收。

public class ChatB {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		
		// 客户端B监听7788端口
		try (DatagramSocket socket = new DatagramSocket(7788)){
			
			// 提前创建两个Packet数据包,分别用于发送和接收
			DatagramPacket sendPacket = new DatagramPacket(
					new byte[1024], 1024, // 数据
					new InetSocketAddress("192.168.1.5", 8899) // 目的地
					);
			DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
			
		while(true) {
			// 发送
			System.out.print("我说:");
			String sendContent = input.nextLine();
			
			sendPacket.setData(sendContent.getBytes());
			socket.send(sendPacket);
			
			if(sendContent.equals("over")) {
				System.out.println("你退出聊天!!!");
				return;
			}
			
			// 接收
			socket.receive(receivePacket);
			String receiveContent = new String(receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength());
			if(receiveContent.equals("over")) {
				System.out.println("对方已退出聊天!!!");
				break;
			}
			System.out.println("她说:" + receiveContent);
		}
			
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

接收端

        发送端与接收端思路整体一致,只不过接收端是先收再发。

public class ChatA {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		
		//客户端B监听8899端口
		try (DatagramSocket socket = new DatagramSocket(8899)){
			
			// 提前创建两个Packet数据包,分别用于发送和接收
			DatagramPacket sendPacket = new DatagramPacket(
					new byte[1024], 1024, // 数据
					new InetSocketAddress("192.168.1.5", 7788) // 目的地
					);
			DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
				
		while(true) {
			// 接收
			socket.receive(receivePacket);
			String receiveContent = new String(receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength());
			if(receiveContent.equals("over")) {
				System.out.println("对方已退出聊天!!!");
				break;
			}
			System.out.println("她说:" + receiveContent);
			
			// 发送
			System.out.print("我说:");
			String sendContent = input.nextLine();
			sendPacket.setData(sendContent.getBytes());
			socket.send(sendPacket);
			if(sendContent.equals("over")) {
				System.out.println("你退出聊天!!!");
				return;
			}
		}
				
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

运行结果显示:

 

 实现多人对话

        基于双人对话的基础,在进行整改。

        将组内成员的IP和姓名可以存入一个Map集合,在输出的时候调用,可以西安市这句话是哪位成员说的。再创建一个List集合,存入对应人数的DatagramPacket对象。选出一位管理员作为发送端,其他成员作为接收端。每个人固定自己的端口号进行监听,在发送信息时,通过for()循环遍历输出,向每位成员发送。

管理员(发送端)

public class Work {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		
		Map<String, String> map = new HashMap<String, String>(){
			{
				put("/192.168.254.153", "**");
				put("/192.168.254.181", "**");
				put("/192.168.254.150", "**");
				put("/192.168.254.163", "**");
			}
		};
		
		List<DatagramPacket> list1 = new ArrayList<DatagramPacket>();
		try (DatagramSocket socket = new DatagramSocket(5555)){
			
			// 发送
			DatagramPacket sendPacket1 = new DatagramPacket(new byte[1024], 1024, new InetSocketAddress("192.168.254.153", 9999));
			DatagramPacket sendPacket2 = new DatagramPacket(new byte[1024], 1024, new InetSocketAddress("192.168.254.181", 8888));
			DatagramPacket sendPacket3 = new DatagramPacket(new byte[1024], 1024, new InetSocketAddress("192.168.254.150", 7777));
			DatagramPacket sendPacket4 = new DatagramPacket(new byte[1024], 1024, new InetSocketAddress("192.168.254.163", 6666));
			list1.add(sendPacket1);
			list1.add(sendPacket2);
			list1.add(sendPacket3);
			list1.add(sendPacket4);
			
			// 接收
			DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
			
		while(true) {
			// 发送
			System.out.print("我说:");
			String sendContent = input.nextLine();
			for(int i = 0; i < list1.size(); i++) {
				list1.get(i).setData(sendContent.getBytes());
				socket.send(list1.get(i));
			}
			
			// 接收
			socket.receive(receivePacket);
			String receiveContent = new String(receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength());
			String ip = receivePacket.getAddress().toString();
			if(receiveContent.equals("over")) {
				System.out.println(map.get(ip) + "已退出聊天!!!");
				break;
			}
			System.out.println(map.get(ip) + "说:" + receiveContent);
		}
			
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

组内成员(接收端)

public class Work {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		
		Map<String, String> map = new HashMap<String, String>(){
			{
				put("/192.168.254.153", "**");
				put("/192.168.254.181", "**");
				put("/192.168.254.150", "**");
				put("/192.168.254.188", "**");
			}
		};
		
		List<DatagramPacket> list1 = new ArrayList<DatagramPacket>();
		try (DatagramSocket socket = new DatagramSocket(5555)){
			
			// 发送
			DatagramPacket sendPacket1 = new DatagramPacket(new byte[1024], 1024, new InetSocketAddress("192.168.254.153", 9999));
			DatagramPacket sendPacket2 = new DatagramPacket(new byte[1024], 1024, new InetSocketAddress("192.168.254.181", 8888));
			DatagramPacket sendPacket3 = new DatagramPacket(new byte[1024], 1024, new InetSocketAddress("192.168.254.150", 7777));
			DatagramPacket sendPacket4 = new DatagramPacket(new byte[1024], 1024, new InetSocketAddress("192.168.254.188", 5555));
			list1.add(sendPacket1);
			list1.add(sendPacket2);
			list1.add(sendPacket3);
			list1.add(sendPacket4);
			
			// 接收
			DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
			
		while(true) {
            // 接收
			socket.receive(receivePacket);
			String receiveContent = new String(receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength());
			String ip = receivePacket.getAddress().toString();
			if(receiveContent.equals("over")) {
				System.out.println(map.get(ip) + "已退出聊天!!!");
				break;
			}
			System.out.println(map.get(ip) + "说:" + receiveContent);
		}


			// 发送
			System.out.print("我说:");
			String sendContent = input.nextLine();
			for(int i = 0; i < list1.size(); i++) {
				list1.get(i).setData(sendContent.getBytes());
				socket.send(list1.get(i));
			}
			
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

  • 29
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: TCP(传输控制协议)是一种面向连接的协议,它为两台计算机之间的通信提供了可靠的通信机制。它为应用程序提供了可靠的字节流传输服务,可以保证数据传输的可靠性和安全性。UDP(用户数据报协议)是一种无连接的协议,它可以将数据报文发送到网络上的任何主机,而不需要建立连接。它提供了一个不可靠的数据传输服务,没有保证数据的可靠性和安全性。 ### 回答2: TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是两种常见的传输层协议,它们在Java编程有一些明显的区别。 首先,TCP是一种面向连接的协议,而UDP是一种无连接的协议。在TCP,客户端和服务器之间建立连接,并且在数据传输之前进行握手和断开连接。而在UDP,数据包可以直接发送给接收方,无需建立连接。因此,在需要实时传输并且延迟较低的应用UDP会更适合,而在需要可靠性和顺序性的应用TCP更可靠。 其次,TCP提供可靠的数据传输机制,确保数据的完整性和正确性。它使用了序列号和确认机制来保证数据的顺序和完整性,并且具有自动重发机制,以处理丢失的数据包。而UDP不提供这些机制,数据包可能会在传输过程丢失或乱序,因此需要应用程序自己处理这些问题。 此外,TCP是一种面向字节流的协议,它将数据视为连续的字节流,没有明确的消息边界。因此,在使用TCP传输数据时,需要应用程序设计专门的协议来区分不同的消息。而UDP是一种面向数据报的协议,每个UDP数据包都有固定的大小,可以直接发送和接收,应用程序可以很容易地区分不同的数据包。 最后,TCP具有较高的延迟和较高的网络开销。由于建立连接、保证可靠性等机制的存在,TCP会引入一定的延迟和网络开销。而UDP没有这些机制,因此具有较低的延迟和网络开销。 综上所述,TCPUDPJava的使用有一些明显的区别。TCP适合需要可靠性和顺序性的应用,而UDP适合实时传输和延迟较低的应用。根据实际需求选择合适的协议可以提高程序的性能和效率。 ### 回答3: TCP(传输控制协议)和UDP(用户数据报协议)都是在网络通信使用的传输层协议,它们之间有以下几个区别: 1. 连接和无连接:TCP是一种面向连接的协议,通信双方在数据传输之前需要建立连接,确保数据的可靠传输。而UDP是一种无连接的协议,通信双方之间不需要建立连接,可以直接发送数据。 2. 可靠性:TCP提供可靠的数据传输,通过使用确认和重传机制来确保数据的完整性和可靠性。而UDP不提供可靠性保证,发送端发送的数据包是否能到达接收端是不做任何保证的,也无法检测和恢复丢失的数据包。 3. 数据包的顺序:TCP保证数据包的顺序,即数据包按照发送顺序传输到接收端,接收端可以按照相同的顺序重新组装数据。而UDP不保证数据包的顺序,可能会导致接收端按照不同的顺序接收数据包。 4. 传输效率:TCP需要维护连接状态、进行拥塞控制等,因而比UDP的传输效率稍低。UDP没有这些额外的负载,传输效率较高。 5. 适用场景:TCP适用于需要可靠传输、传输量较大、带宽较高的应用场景,比如网页浏览、邮件收发、文件传输等。UDP适用于实时性要求较高、传输量较小、带宽较低的应用场景,比如语音通话、视频传输、实时游戏等。 综上所述,TCPUDP在可靠性、连接方式、数据包顺序和传输效率等方面存在着明显的差异,并且适用于不同的应用场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值