一.什么是TCP与UDP
TCP与UDP是计算机网络中五层模型的运输层协议。
TCP 协议:是传输控制协议,它是面向连接的协议,支持可靠传输和双向通信。它在传输数据之前需要先建立连接,建立连接后才能传输数据,传输完后还需要断开连接。TCP协议之所以能保证数据的可靠传输,是通过接收确认、超时重传这些机制实现的。并且,TCP协议允许双向通信,即通信双方可以同时发送和接收数据。
UDP协议:(User Datagram Protocol)是一-种数据报文协议,它是无连接协议,不保证可 靠传输。 因为UDP协议在通信前不需要建立连接,因此它的传输效率比TCP高,而且UDP 协议比TCP协议要简 单得多
二.什么是Socket
在我们进行网络编程时,都会接触到一个名叫scoket的概念,应用程序通过Scoket来建立远程连接,Socket通过内部封装好的协议把数据传输到网络。
为什么需要Socket 进行网络通信?因为仅仅通过IP地址进行通信是不够的,同一台计算机同- -时间会运行多个网络应用程序,例如浏览器、QQ、邮件客户端等。当操作系统接收到一个数据包的时候,如果 只有IP地址,它没法判断应该发给哪个应用程序,所以,操作系统抽象出Socket 接口,每个应用程序需 要各自对应到不同的Socket,数据包才能根据 Socket正确地发到对应的应用程序。
使用Socket进行网络编程时,本质上就是两个进程之间的网络通信。其中一个进程必须充当服务器端,它会主动监听某个指定的端口,另一个进程必须充当客户端,它必须主动连接服务器的IP地址和指定端口,如果连接成功,服务器端和客户端就成功地建立了一个TCP连接,双方后续就可以随时发送和接收,因此,当Socket 连接成功地在服务器端和客户端之间建立后:对服务器端来说,它的Socket是指定的IP地址和指定的端口号。
三.TCP网络编程简单实现
TCP的编程实现客户端与服务器端交互,是通过建立连接,从而利用“流”来进行数据交换的,即使用字节输入/输出流,把要发送的数据,存储到字节数组,通过“流”来实现发送与接收。
接下来我们以客户端向服务器端上传图片的简单例子来说明TCP编程。
1.建立服务器端
第一步:建立socket,服务器端使用java提供的ServerSocket创建于客户端交互的对象,并且监听指定端口。
第二步:建立与客户端的连接,创建一个Scoket,调用accept()方法,与客户端建立连接,并且获取客户端的IP地址
第三步:创建输入流,来读取客户端上传的照片使用:getInputStream()
第四步:创建输出流,对客户端上传的图片,写入到本地,我们这里使用BufferedInputStream来实现建立字节字节数组对传过来的数据进行批量写入。
第六步:当我们成功接收到客户端上传的图片以后,要给客户端发送一个反馈,这时,我们需要创建一个输出流,向客户端进行反馈。
完整的实例代码如下:
try {
//ServerSocket:服务器端进行通信的对象
ServerSocket server = new ServerSocket(8848);
//死循环:不断接收客户端的连接
while(true) {
//服务器进入“等待”状态
//如果有客户端连接时,该方法1返回客户端的Socket
Socket cilent = server.accept();
InetAddress clientNetAddress = cilent.getInetAddress();
System.out.println("客户端" +clientNetAddress + "正在连接.......");
// 接收来自客户端上传的图片
// 输入流:读取来自客户端发送的图片文件流
// 输出流:写入本地图片
String imgName = clientNetAddress.getHostAddress().replaceAll("\\.", "\\-") + ".jpg";
try(InputStream in = cilent.getInputStream();
BufferedInputStream bis = new BufferedInputStream(in);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d:\\test\\aaa\\" + imgName))){
// 每次读取来自客户端的图片文件流
// 写入本地
byte[] buff = new byte[1024];
int len = -1;
while((len = bis.read(buff)) != -1) {
bos.write(buff,0,len);
}
System.out.println("图片读取完毕!");
// 输出提示信息 => 客户端
try(BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(cilent.getOutputStream()));){
writer.write("上传完毕!");
writer.newLine();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
我们这里用到了一个死循环,是为了保证服务器端可以持续不断地接收来自客户端上传的图片。
2.建立客户端
相比服务器端,客户端就简单很多
第一步:创建Socket,用于和服务器端进行连接的组件,传入服务器端的IP地址与端口号。
第二步:创建一个输出流,将要上传的图片发送至服务器端。
第三步:创建输入流,来获取将要发送的本地的图片的路径,我们这里也使用BufferedInputStream来对本地图片进行一个批量读取,将读取到的内容发送至服务器端。
这里值得注意的是:使用cilent.shutdownOutput(),输出暂时结束(Socket没有关闭)
第四步:我们等待服务器端接收成功后,发生给我们一个反馈,我们需要创建一个Socket的输入流,来接收服务器端对我们的反馈。
基本实现代码如下:
//Socket:客户端进行通讯的组件
//本地图片获取 = > 通过输出流,发送(输出)至服务器
//OutputStream:输出流,读取到本地图片文件流,发送(输出)至服务器
//BufferedInputStream:输入流,读取本地文件
try (
Socket cilent = new Socket("192.168.254.188",8848);
OutputStream out = cilent.getOutputStream();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\数据分析\\2017年第一季度各产业生产总值占比饼图.png"))) {
//每次读取到1024字节
byte[] buff = new byte[1024];
int len = -1;
while((len = bis.read(buff)) != -1) {
//将读取到的内容,通过输出流发送至服务器
out.write(buff);
}
//输出暂时结束(Socket没有关闭)
cilent.shutdownOutput();
//读取来自服务器的反馈
try(BufferedReader reader = new BufferedReader(new InputStreamReader(cilent.getInputStream()));){
String reply = reader.readLine();
System.out.println("服务器的反馈:" + reply);
}
} catch (IOException e) {
e.printStackTrace();
}
四.UDP网络编程简单实现
UDP和TCP相比,就简单的多,因为UDP不需要建立连接,也就没有区分哪一个是客户端哪一个是服务器端,是依靠数据包来进行实现的,数据包也是收一个发一个,不存在使用流的概念。
UDP也是需要使用Socket来监听端口的的,不过它使用的是java提供的DatagramSocket。
我们以一个简易的聊天室的案例,来实现简单的UDP编程:
第一步:创建Scanner 来进行对话的输入
第二步:创建Socket,来监听端口。
第三步:创建两个数据包来实现对话的发送与接收。
1.发送:
这里使用的是DatagramPacket,传入一个字节数组,并且传入将要发送的IP地址与端口号
使用socket.send(sendPacket),来进行发送
try (DatagramSocket socket = new DatagramSocket(6666);) {
//提前创建两个Packet数据包,分别用于发送和接收
DatagramPacket sendPacket =
new DatagramPacket(
new byte[1024],1024, //数据
new InetSocketAddress("192.168.254.153",7788) //目的地
);
while(true) {
//发送
System.out.print("你说:");
String sendContent = input.nextLine();
sendPacket.setData(sendContent.getBytes());
socket.send(sendPacket1);
if(sendContent.equals("滚")) {
System.out.println("我不想跟你讲话");
return;
}
socket.send(sendPacket);
sendPacket.setData(sendContent.getBytes())
是将输入的数据存入数据包中
2.接收
首先建立一个接收的数据包,传入一个自定义的字节数组,用来接收数据
DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
使用socket.receive(receivePacket),方法来进行数据包的接收
由于接收到的是一个数据包,我们如果想正常显示,就要把它变成字符串
//packet.getData():获取数据包中的“数据”(字节数组)
//packet.getOffset():获取数据包中的“读取位置”
//packet.getLength():获取数据包中的“长度”
String receiveContent = new String(receivePacket.getData(),receivePacket.getOffset(),receivePacket.getLength());
当我们想要不停地与对方进行聊天时,就可以写一个死循环while(true){},来一直输入内容,直到输入关键字,程序才会退出。
代码实现:
//接收
socket.receive(receivePacket);
String receiveContent = new String(receivePacket.getData(),receivePacket.getOffset(),receivePacket.getLength());
if(receiveContent.equals("滚")) {
System.out.println("他不想跟你说话!");
break;
}
system.out.println("他说:" + receiveContent);
}
运行结果: