#TCP连接
TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。
#TCP通过三次握手建立连接
1.客户端发送SYN(SEQ=x)报文给服务端,进入SYN_SEND状态
2.服务端收到SYN(SEQ=y) ACK(ACK=x+1)报文,回应一个SYN报文,进入SYN_RECV状态
3.客户端收到服务端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态
(至此三次握手完成,TCP客户端和服务器端成功建立连接,可以开始传输数据)
示例代码如下:
tcp server端:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
* * 实现TCP服务器程序
* * 表示服务器程序的类 java.net.ServerSocket
* * 构造方法:
* ServerSocket(int port) 传递端口号
* * 很重要的事情:必须要获得客户端的套接字对象Socket
* Socket accept()
*/
public class TcpServer {
public static void main(String[] args)throws IOException {
ServerSocket server=new ServerSocket(8888);
while(true) {
//调用服务器套接字对象中的方法accept()获取客户端套接字对象
Socket socket=server.accept();
//通过客户端套接字对象,socket获取字节输入流,读取的是客户端发送来的数据
InputStream in=socket.getInputStream();
byte[] data=new byte[1024];
int len=in.read(data);
System.out.println("tcp server: " + new String(data,0,len));
//服务器向客户端回数据,字节输出流,通过客户端套接字对象获取字节输出流
OutputStream out=socket.getOutputStream();
out.write("hello,tcp client.i am tcp server.".getBytes());
// socket.close();
// server.close();
}
}
}
tcp client端:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/*
* * 实现TCP客户端,接收到服务器
* * 和服务器实现数据交换
* * 实现TCP客户端程序的类 java.net.Socket
* * 构造方法:
* Socket(String host,int port) 传服务器IP和端口号
* 注意:构造方法只要运行,就会和服务器进行连接,连接失败,抛出异常
* OutputStream getOutputStream() 返回套接字的输出流
* * 作用:将数据输出,输出到服务器
* InputStream getInputStream() 返回套接字的输入流
* * 作用:从服务端读取数据
* * 客户端服务器数据交换,必须使用套接字对象Socket中的获取的IO流,自己new流,不行
*/
public class TcpClient {
public static void main(String[] args) throws IOException, InterruptedException{
while(true) {
Thread t = new Thread(new Runnable() {
public void run() {
try {
//创建Socket对象,连接服务器
Socket socket=new Socket("127.0.0.1",8888);
//通过客户端的套接字对象Socket方法,获取字节输出流,将数据写向服务器
OutputStream out=socket.getOutputStream();
out.write("hello,tcp server.i am client.".getBytes());
//读取服务器发回的数据,使用socket套接字对象中的字节输入流
InputStream in=socket.getInputStream();
byte[] data=new byte[1024];
int len=in.read(data);
System.out.println("tcp client:" + new String(data,0,len));
// socket.close();
} catch (Exception e) {
// TODO: handle exception
}
}
});
Thread.sleep(1000);
t.start();
}
}
}
#UDP连接
UDP是面向无连接的通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接
示例代码如下:
UDP server端:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UdpServer {
public static void main(String[] args) throws Exception {
//创建socket
DatagramSocket socket = new DatagramSocket(7000);
//创建packet
DatagramPacket packet = new DatagramPacket(new byte[1024],1024);
//接收数据
// while(true){
socket.receive(packet);
//获取数据
byte[] arr = packet.getData();
System.out.println("===="+new String(arr,"utf-8"));
//获取有效的字节个数
int len = packet.getLength();
//获取Ip地址
String ip = packet.getAddress().getHostAddress();
//获取端口号
int port = packet.getPort();
System.out.println(ip+":"+port+":"+new String(arr,0,len));
// }
}
}
UDP client端:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class UdpClient {
public static void main(String[] args) throws Exception {
//创建键盘录入对象
// Scanner scan = new Scanner(System.in);
DatagramSocket socket = new DatagramSocket();
// while(true){
String line ="A fox may grow grey, but never good.";
if("exit".equals(line)){
// break;
}else{
DatagramPacket packet = new DatagramPacket(line.getBytes(),
line.getBytes().length,InetAddress.getByName("192.168.1.82"),7000);
//发送数据
socket.send(packet);
}
System.out.println("发送成功");
// }
//关闭socket
// socket.close();
}
}
udp介绍更详细内容: UDP协议详细讲解<以下部分内容引用自"Linux公社"公众号,可直接关注该公众号进行查阅,如有侵权,请联系删除>
UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。
由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议。UDP的交换过程如下图所示。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191118142903991.png)
1.1 DatagramPacket
前面介绍了UDP是一种面向无连接的协议,因此,在通信时发送端和接收端不用建立连接。UDP通信的过程就像是货运公司在两个码头间发送货物一样。在码头发送和接收货物时都需要使用集装箱来装载货物,UDP通信也是一样,发送和接收的数据也需要使用“集装箱”进行打包,为此JDK中提供了一个DatagramPacket类,该类的实例对象就相当于一个集装箱,用于封装UDP通信中发送或者接收的数据。
想要创建一个DatagramPacket对象,首先需要了解一下它的构造方法。在创建发送端和接收端的DatagramPacket对象时,使用的构造方法有所不同,接收端的构造方法只需要接收一个字节数组来存放接收到的数据,而发送端的构造方法不但要接收存放了发送数据的字节数组,还需要指定发送端IP地址和端口号。
接下来根据API文档的内容,对DatagramPacket的构造方法进行逐一详细地讲解。
![在这里插入图片描述](https://img-blog.csdnimg.cn/2019111814283977.png)
使用该构造方法在创建DatagramPacket对象时,指定了封装数据的字节数组和数据的大小,没有指定IP地址和端口号。很明显,这样的对象只能用于接收端,不能用于发送端。因为发送端一定要明确指出数据的目的地(ip地址和端口号),而接收端不需要明确知道数据的来源,只需要接收到数据即可。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191118143003157.png)
使用该构造方法在创建DatagramPacket对象时,不仅指定了封装数据的字节数组和数据的大小,还指定了数据包的目标IP地址(addr)和端口号(port)。该对象通常用于发送端,因为在发送数据时必须指定接收端的IP地址和端口号,就好像发送货物的集装箱上面必须标明接收人的地址一样。
上面我们讲解了DatagramPacket的构造方法,接下来对DatagramPacket类中的常用方法进行详细地讲解,如下表所示。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191118143032965.png)
1.2 DatagramSocket
DatagramPacket数据包的作用就如同是“集装箱”,可以将发送端或者接收端的数据封装起来。然而运输货物只有“集装箱”是不够的,还需要有码头。在程序中需要实现通信只有DatagramPacket数据包也同样不行,为此JDK中提供的一个DatagramSocket类。DatagramSocket类的作用就类似于码头,使用这个类的实例对象就可以发送和接收DatagramPacket数据包,发送数据的过程如下图所示。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191118143102546.jpg)
在创建发送端和接收端的DatagramSocket对象时,使用的构造方法也有所不同,下面对DatagramSocket类中常用的构造方法进行讲解。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191118143125773.png)
该构造方法用于创建发送端的DatagramSocket对象,在创建DatagramSocket对象时,并没有指定端口号,此时,系统会分配一个没有被其它网络程序所使用的端口号。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191118143146951.png)
该构造方法既可用于创建接收端的DatagramSocket对象,又可以创建发送端的DatagramSocket对象,在创建接收端的DatagramSocket对象时,必须要指定一个端口号,这样就可以监听指定的端口。
上面我们讲解了DatagramSocket的构造方法,接下来对DatagramSocket类中的常用方法进行详细地讲解。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191118143208468.png)
1.3 UDP实现1.3.1 案例代码二:
package com.linuxmi_02;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/*
* 使用UDP协议发送数据
创建发送端Socket对象
创建数据并打包
发送数据
释放资源
*
* DatagramSocket:此类表示用来发送和接收数据,基于UDP协议的
*
* DatagramSocket() :创建Socket对象并随机分配端口号
* DatagramSocket(int port) :创建Socket对象并指定端口号
*/
public class SendDemo {
public static void main(String[] args) throws IOException {
//创建发送端Socket对象
DatagramSocket ds = new DatagramSocket();
//创建数据并打包
/*
* DatagramPacket :此类表示数据报包
* 数据 byte[]
* 设备的地址 ip
* 进程的地址 端口号
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
*/
String s = "hello udp,im comming!";
byte[] bys = s.getBytes();
int length = bys.length;
InetAddress address = InetAddress.getByName("linuxmi");//发送给当前设备
int port = 8888;
//打包
DatagramPacket dp = new DatagramPacket(bys,length,address,port);
//发送数据
ds.send(dp);
//释放资源
ds.close();
}
}
package com.linuxmi_02;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/*
* 使用UDP协议接收数据
创建接收端Socket对象
接收数据
解析数据
输出数据
释放资源
*/
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
//创建接收端Socket对象
DatagramSocket ds = new DatagramSocket(8888);
//接收数据
//DatagramPacket(byte[] buf, int length)
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys,bys.length);
System.out.println(1);
ds.receive(dp);//阻塞
System.out.println(2);
//解析数据
//InetAddress getAddress() : 获取发送端的IP对象
InetAddress address = dp.getAddress();
//byte[] getData() :获取接收到的数据,也可以直接使用创建包对象时的数组
byte[] data = dp.getData();
//int getLength() :获取具体收到数据的长度
int length = dp.getLength();
//输出数据
System.out.println("sender ---> " + address.getHostAddress());
//System.out.println(new String(data,0,length));
System.out.println(new String(bys,0,length));
//释放资源
ds.close();
}
}