Java网络编程_TCP编程以及UDP编程
首先理清一个概念:网络编程不等于网站编程,网络编程即使用套接字来达到进程间通信,现在一般称为TCP/IP编程。
计算机网络
计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
1.TCP
传输控制协议(Transmission Control Protocol)。
TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”。
2.UDP
用户数据报协议(User Datagram Protocol)。
数据报(Datagram):网络传输的基本单位
UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。
由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时,不建议使用UDP协议。
特点:数据被限制在64kb以内,超出这个范围就不能发送了。
TCP编程
首先我们先要了解什么是 Socket ?Socket是一个抽象概念,一个应用程序通过一个Socket来建立一个远程连接,而Socket内部通过TCP/IP协议把数据传输到网络。
服务器端
public class Server {public static void main(String[] args) throws IOException {//这里我们没有指定IP地址,表示在计算机的所有网络接口上进行监听。ServerSocket ss = new ServerSocket(0000); // 监听指定端口System.out.println("server is running...");while (true) {Socket sock = ss.accept();// 使用Socket流进行网络通信 //如果ServerSocket监听成功,我们就使用一个无限循环来处理客户端的连接,注意到代码ss.accept()表示每当有新的客户端连接进来后,就返回一个Socket实例,这个Socket实例就是用来和刚连接的客户端进行通信的。System.out.println("connected from " + sock.getRemoteSocketAddress());}}
}
客户端
public class Client {public static void main(String[] args) throws IOException {// 连接指定服务器和端口Socket sock = new Socket("localhost", 0000); //注意上述代码的服务器地址是"localhost",表示本机地址,端口号是0000。如果连接成功,将返回一个Socket实例,用于后续通信。 // 使用Socket流进行网络通信// 关闭sock.close();System.out.println("disconnected.");}
}
当Socket连接创建成功后,无论是服务器端,还是客户端,我们都使用Socket实例进行网络通信。因为TCP是一种基于流的协议,因此,Java标准库使用InputStream和OutputStream来封装Socket的数据流,这样我们使用Socket的流,和普通IO流类似:
// 用于读取网络数据:
InputStream in = sock.getInputStream();// 用于写入网络数据:
OutputStream out = sock.getOutputStream();
写入网络数据时,必须要调用 flush() 方法。如果不调用flush(),我们很可能会发现,客户端和服务器都收不到数据,这并不是Java标准库的设计问题,而是我们以流的形式写入数据的时候,并不是一写入就立刻发送到网络,而是先写入内存缓冲区,直到缓冲区满了以后,才会一次性真正发送到网络,这样设计的目的是为了提高传输效率。如果缓冲区的数据很少,而我们又想强制把这些数据发送到网络,就必须调用flush()强制把缓冲区数据发送出去。
案例:基于TCP编程实现聊天
服务端:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;public class ChatServer {public static void main(String[] args) {Map<String, String> chatMap = new HashMap<String, String>() {{put("你好", "你好呀!");put("hi", "嗨!");put("hello", "哈喽!"); }};try (ServerSocket server = new ServerSocket(0000)) {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();}}
}
客户端:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;public class ChatClient {public static void main(String[] args) {Scanner input = new Scanner(System.in);while (true) {// 创建Socket,连接至服务器try (Socket client = new Socket("192.168.254.178", 0000);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("Game Over!!!");}
}
UDP编程(和TCP编程相比,UDP编程就简单得多,因为UDP没有创建连接,数据包也是一次收发一个,所以没有流的概念。)
JAVA中UDP的API类:
1.InetAddress:用于描述和包装一个 Internet IP地址
方法名 | 返回值 |
getLocalhost() | 返回封装本地地址的实例 |
getAllByName(String host) | 返回封装Host地址的InetAddress实例数组 |
getByName(String host) | 返回一个封装Host地址的实例。其中,Host可以是域名或者是一个合法的IP地址。 |
InetAddress.getByAddress(addr) | 根据地址串返回InetAddress实例 |
2.DatagramSocket: 用于接收和发送UDP的Socket实例。
DatagramSocket() |
|
|
DatagramSocket(int port) | 创建实例,并固定监听Port端口的报文。通常用于服务端 | |
DatagramSocket(int port, InetAddress localAddr) | 这是个非常有用的构建器,当一台机器拥有多于一个IP地址的时候,由它创建的实例仅仅接收来自LocalAddr的报文。 | |
receive(DatagramPacket d) | 接收数据报文到d中。receive方法产生一个“阻塞”。“阻塞”是一个专业名词,它会产生一个内部循环,使程序暂停在这个地方,直到一个条件触发。 | |
send(DatagramPacket dp) | 发送报文dp到目的地 | |
setSoTimeout(int timeout) | 设置超时时间,单位为毫秒。 | |
close() | 应用程序退出的时候,通常会主动释放资源,关闭Socket,但是由于异常地退出可能造成资源无法回收。所以,应该在程序完成时,主动使用此方法关闭Socket,或在捕获到异常抛出后关闭Socket。 |
3.DatagramPacket:用于处理报文,它将Byte数组、目标地址、目标端口等数据包装成报文或者将报文拆卸成Byte数组。
DatagramPacket(byte[] buf, int length) | 将数据包中Length长的数据装进Buf数组,一般用来接收客户端发送的数据 |
DatagramPacket(byte[] buf, int offset, int length) | 将数据包中从Offset开始、Length长的数据装进Buf数组 |
DatagramPacket(byte[] buf, int length, InetAddress clientAddress, int clientPort) | 从Buf数组中,取出Length长的数据创建数据包对象,目标是clientAddress地址,clientPort端口, 通常用来发送数据给客户端。 |
DatagramPacket(byte[] buf, int offset, int length, InetAddress clientAddress, int clientPort) | 从Buf数组中,取出Offset开始的、Length长的数据创建数据包对象,目标是clientAddress地 址,clientPort端口,通常用来发送数据给客户端。 |
getData() | 从实例中取得报文的Byte数组编码。 |
setDate(byte[] buf) | 将byte数组放入要发送的报文中。 |
服务器端
DatagramSocket ds = new DatagramSocket(6666); // 监听指定端口
while (true) { // 无限循环// 数据缓冲区:byte[] buffer = new byte[1024];DatagramPacket packet = new DatagramPacket(buffer, buffer.length);ds.receive(packet); // 收取一个UDP数据包// 收取到的数据存储在buffer中,由packet.getOffset(), packet.getLength()指定起始位置和长度// 将其按UTF-8编码转换为String:String s = new String(packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8);// 发送数据:byte[] data = "ACK".getBytes(StandardCharsets.UTF_8);packet.setData(data);ds.send(packet);
}
客户端
DatagramSocket ds = new DatagramSocket();
ds.setSoTimeout(1000);
ds.connect(InetAddress.getByName("localhost"), 6666); // 连接指定服务器和端口// 发送:
byte[] data = "Hello".getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length);
ds.send(packet);// 接收:
byte[] buffer = new byte[1024];
packet = new DatagramPacket(buffer, buffer.length);
ds.receive(packet);
String resp = new String(packet.getData(), packet.getOffset(), packet.getLength());
ds.disconnect();
客户端创建DatagramSocket实例时并不需要指定端口,而是由操作系统自动指定一个当前未使用的端口。紧接着,调用setSoTimeout(1000)设定超时1秒,意思是后续接收UDP包时,等待时间最多不会超过1秒,否则在没有收到UDP包时,客户端会无限等待下去。这一点和服务器端不一样,服务器端可以无限等待,因为它本来就被设计成长时间运行。注意到客户端的DatagramSocket还调用了一个connect()方法“连接”到指定的服务器端。不是说UDP是无连接的协议吗?为啥这里需要connect()?这个connect()方法不是真连接,它是为了在客户端的DatagramSocket实例中保存服务器端的IP和端口号,确保这个DatagramSocket实例只能往指定的地址和端口发送UDP包,不能往其他地址和端口发送。这么做不是UDP的限制,而是Java内置了安全检查。如果客户端希望向两个不同的服务器发送UDP包,那么它必须创建两个DatagramSocket实例。后续的收发数据和服务器端是一致的。通常来说,客户端必须先发UDP包,因为客户端不发UDP包,服务器端就根本不知道客户端的地址和端口号。如果客户端认为通信结束,就可以调用disconnect()断开连接。注意:disconnect()也不是真正地断开连接,它只是清除了客户端DatagramSocket实例记录的远程服务器地址和端口号.这样,DatagramSocket实例就可以连接另一个服务器端。
案例实现
package com.zou.udp;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;//客户端
public class wordClient {public static void main(String[] args) {Scanner input = new Scanner(System.in);while (true) {// 发送(向服务器发送一个英文单词)String word = input.nextLine();// 英文单词if (word.equals("end")) {break;}// 创建基于UDP协议的DatagramSocket对象try (DatagramSocket clientSocket = new DatagramSocket()) {// timeout超时clientSocket.setSoTimeout(20000);// 连接服务器(服务器IP和端口)clientSocket.connect(new InetSocketAddress("192.168.254.187", 8877));// 获取英文单词字符串的字节数组byte[] wordBuff = word.getBytes(StandardCharsets.UTF_8);// 封装成DatagramPacket对象(数据包)DatagramPacket packet = new DatagramPacket(wordBuff, wordBuff.length);// 发送数据包clientSocket.send(packet);// 读取byte[] resultBuff = new byte[1024];DatagramPacket resultPacket = new DatagramPacket(resultBuff, resultBuff.length);clientSocket.receive(resultPacket);String result = new String(resultPacket.getData(), resultPacket.getOffset(), resultPacket.getLength());System.out.println("来自服务器的中文释义:" + result);} catch (IOException e) {e.printStackTrace();}}}
}
package com.zou.udp;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.HashMap;
import java.util.Map;//服务器端
public class wordServer {public static void main(String[] args) {Map<String, String> map = new HashMap<String, String>() {{put("one", "壹");put("two", "贰");put("three", "叁");put("four", "肆");put("five", "伍");put("six", "陆");}};try (// 服务器监听8877端口DatagramSocket serverSocket = new DatagramSocket(8877)) {while (true) {// 准备“空”数据包byte[] buff = new byte[1024]; // 原始的字节数组DatagramPacket packet = new DatagramPacket(buff, buff.length);// 读取(客户端发送的英文单词)// 接收数据包serverSocket.receive(packet);// 获取数据包中的"数据"(字节数组) packet.getData()// 获取数据包中的"读取位置"(int类型):packet.getOffset()// 获取数据包中的"长度":packet.getLength()String word = new String(packet.getData(), packet.getOffset(), packet.getLength());System.out.println("【服务器】:获取来自客户端的单词" + word);String chinese = map.get(word);if (chinese == null) {chinese = "未知结果";}// 发送(向客户端发送中文释义)byte[] resultbuff = chinese.getBytes();packet.setData(resultbuff);// 重新设置数据内容serverSocket.send(packet);}} catch (IOException e) {e.printStackTrace();}}
}
Java网络编程_TCP编程以及UDP编程相关推荐
- Java网络编程之TCP、UDP
Java网络编程之TCP.UDP 2014-11-25 15:23 513人阅读 评论(0) 收藏 举报 分类: java基础及多线程(28) 版权声明:本文为博主原创文章,未经博主允许不得转载. J ...
- python的基础网络编程是下列_Python入门基础之网络编程、socket编程、TCP、UDP编程...
忙了两天,继续更文!希望多多支持. 套接字 套接字是一种具有之前所说的"通讯端点"概念的计算机网络数据结构.网络化的应用程序在开始任何通讯之前都必需要创建套接字. 套接字有三种: ...
- Python学习笔记(四十六)网络编程(2)— UDP编程
摘抄:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014320049779 ...
- Java网络编程(八)—— 组播和MulticastSocket
Java网络编程(八)-- 组播和MulticastSocket 文章目录 Java网络编程(八)-- 组播和MulticastSocket 什么是组播 MulticastSocket类 总述 构造方 ...
- Linux网络编程——原始套接字编程
Linux网络编程--原始套接字编程 转自:http://blog.csdn.net/tennysonsky/article/details/44676377 原始套接字编程和之前的 UDP 编程差不 ...
- 【018】Python全栈日记-UDP编程
从今天开始进行网络的学习,主要以socket编程为主,今天先了解一下网络的基础知识和UDP编程.本次日记很有意思哦,最后模拟了微信聊天. 以下计算机基础知识来源于网络(https://www.cnbl ...
- 【Java网络编程(一)】IP地址、端口、URL、网络爬虫原理、TCP UDP协议
IP地址与域名的获取 package cn.hanquan.test;import java.net.InetAddress; import java.net.UnknownHostException ...
- Java 网络编程(二) 两类传输协议:TCP UDP
两类传输协议:TCP,UDP TCP TCP是Transfer Control Protocol(传输控制协议)的简称,是一种面向连接的保证可靠传输的协议. 在TCP/IP协议中, IP层主要负责网络 ...
- 菜鸟学习笔记:Java提升篇10(网络2——UDP编程、TCPSocket通信、聊天室案例)
菜鸟学习笔记:Java提升篇10(网络2--UDP编程.TCPSocket通信) UDP编程 TCP编程(Socket通信) 单个客户端的连接 多个客户端的连接(聊天室案例) UDP编程 在上一篇中讲 ...
最新文章
- 零基础该如何系统地自学Python编程?五个阶段带你从小白到大佬
- 【FluidSynth】SoundFont 音源文件资料收集 ( SoundFont 规范 | SoundFont 音源下载 | SoundFont 编辑器 | 博客资源 )
- 第1章列表处理——1.1 Lisp列表
- Codeforces 698D Limak and Shooting Points (搜索)
- snakeyaml读取yaml/yml配置文件数据
- java i o流异常问题_第四章 Java的I/O流和异常处理
- eclipse myeclipse 快捷键
- 面向对象编程(1)-类和实例
- Unity3d Shader开发(三)Pass(Fog )
- VS-C++ 调试----调试信息输出至调试器(输出界面)
- 南方cass简码识别大全_cass简码识别教程.doc
- 通过windows的超级终端连接华为交换机
- SaaSBase:最适合小团队轻量级项目管理的软件——Tower
- 电脑wps可以语音录入吗_WPS怎么样?语音速记助你1分钟输入400字
- 史上最全股票指标图文详解
- 王者荣耀同一微信号多个服务器,王者荣耀微信怎么切换账号?打不上省排?那就换个荣耀战区好了...
- html外联式怎么设置,笔记《三》-html引用css的三种方式-内联,嵌入,外联
- SDS新书的来龙去脉 amp;amp; SDS序言 - 倪光南:众筹出书也是一种创新
- 狂妄之人音乐计算机乐谱,天谕手游狂妄之人乐谱_狂妄之人乐谱代码分享_3DM手游...
- 绩效管理工具OKR与GRAD