一、TCP协议
TCP协议是面向连接的协议,在数据传输之前会首先建立逻辑连接,然后在传输数据,可保证数据的无差错数据传输,TCP连接中必须明确客户端和服务端,由客户端向服务端发起连接请求,每次创建连接都要经过三次握手。①客户端向服务端发出请求,②服务端响应客户端,通知客户端已收到请求。③客户端再次向服务端发送确认信息,确认连接。
TCP相关类的使用:
-
ServerSocket类
用于表示服务端。
构造函数:
①ServerSocket()
不带参的默认构造方法,因为没有绑定端口,需要调用bind(SocketAddress endpoint)方法绑定端口才可正常使用。
②ServerSocket(int port)
创建对象时指定端口号,指定为0时系统会自动分配一个没有被占用的端口号。
③ServerSocket(int port,int backlog)
比第二个方法多了一个backlog参数,指在服务器忙时保持连接请求的等待客户数量,不指定默认为50。
④ServerSocket(int port,int backlog,InetAdress bindAddr)
比第三个方法多了一个bindAddr参数,用于指定ip地址,一般用于多网卡多IP的场景。
常用方法:Socket accept()//等待客户连接,在客户连接之前会一直处于连接状态,若有客户连接则返回一个与之对应的Socket。 InetAdress getInetAdress()//返回InetAdress对象,对象中封装了ServerSocket封装的IP地址。 boolean isClosed()//判断ServerSocket对象状态,关闭返回true,打开返回false. void bind(SocketAddress endpoint)//绑定IP地址与端口号。
-
Socket类
用于表示客户端。
构造函数:
①Socket()
创建对象时没有指定ip与端口号,使用还需要调用connect(SocketAddress endpoint)方法,才能与指定的服务端连接,endpoint封装ip与端口号。
②Socket(String host, int port)
创建对象时指定IP与端口号,host为字符串IP。
③Socket(InetAddress address, int port)
与前一个方法区别在于ip地址是使用InetAddress封装。
常用方法:int getPort()//返回与服务端连接的端口号。 InetAddress getLocalAddress()//获取Socket绑定的本地IP地址。 void close()//释放资源. InputStream getInputStream()//返回输入流对象,如果是由服务端的Socket返回,则用于读取客户端数据,反之,则读取客户端数据。 OutputStream getOutputStream()//返回输出流对象,若是有服务端的Socket返回,则用于向客户端发送数据,反之则用于向服务端发送数据。
使用案例:
客户端:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class T_Client {
public static void main(String[] args) throws IOException {
new TCPClient().connect();
}
}
//TCP客户端
class TCPClient{
private static final int PORT = 7001;
public void connect() throws IOException {
//创建Socket对象
Socket client = new Socket(InetAddress.getLocalHost(),PORT);
//给服务端发送信息
OutputStream op =client.getOutputStream();
String str ="hello Server...";
op.write(str.getBytes("UTF-8"));
//关闭输出流,表示发送完成
client.shutdownOutput();
//读取服务端发送的数据
InputStream is = client.getInputStream();
byte[] buf = new byte[1024];
int len = is.read(buf);
System.out.println(new String(buf,0, len));
client.close();
}
}
服务端:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class T_Server {
public static void main(String[] args) throws IOException, InterruptedException {
new TCPServer().listen();
}
}
class TCPServer{
private static final int PORT = 7001;
public void listen() throws IOException, InterruptedException {
ServerSocket serverSocket = new ServerSocket(PORT);
while (true){
//accept(),监听客户端
Socket client = serverSocket.accept();
//使用线程,同时处理多个客户端请求
new Thread(){
@Override
public void run() {
try {
System.out.println("开始与客户端交互数据");
//读取服客户发送的数据
InputStream is = client.getInputStream();
byte[] buf = new byte[1024];
int len = is.read(buf);
System.out.println(new String(buf,0, len));
//获取输出流,给客户端返回消息
OutputStream os = client.getOutputStream();
os.write("hello 早上好".getBytes());
Thread.sleep(10000);
System.out.println("交互结束");
os.close();
client.close();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
}
}
二、UDP协议
UDP协议是无连接通信的协议,发送数据时发送端和接收端不建立逻辑连接,即在发送数据时发送端不会确认接收端是否存在,只管发送数据,不确定数据是否送达,而接收端接收到数据时也不会返回信息给发送端。相较于TCP,UDP协议消耗的资源较小,通信效率高,通常都会用在一些视频、音频的传输和一些允许数据丢失的场景使用,因为UDP协议不能确保数据的完整性,是不可靠的传输,在传输重要数据时不推荐使用UDP协议。
UDP相关类的使用:
-
DatagramPacket类
该类用于封装UDP传输的发送或者接收的数据包。
构造函数:
①DatagramPacket(byte[] buf, int length)
该方法只指定了字节数组与数据大小,没有ip地址和端口号,因为不知道目的地址的ip和端口号,所以不能用于发送端。
②DatagramPacket(byte[] buf, int offset, int length)
该方法比上一方法多了一个offset参数,指接收到的数据放入buf缓冲区时从offset处开始。
③DatagramPacket(byte[] buf, int length, InetAddress addr, int port)
该方法指定了字节数组、数据大小、ip、端口号。明确目的地址,所以一般用于发送端。
④DatagramPacket(byte[] buf, int offset, int length, InetAddress addr, int port)
该方法比上一个方法多了一个偏移量offset,指从数组offset位置开始发送数据。
常用方法:InetAddress getAddress()//返回IP地址,如果是发送端的对象,则返回接收端的IP地址(目的IP),反之则返回发送端的IP。 int getPort()//返回端口号,如果是发送端,则返回接收端的端口号(目的端口号),相反则返回发送端的端口号。 byte[] getData()//返回数据,如果是发送端,则返回将要发送的数据,如果是接收端,则返回接收的数据。 int getLength()//返回数据长度,接收和发送端都可用。
-
DatagramSocket类
该类用于实现数据的发送与接收。
构造函数:
①DatagramSocket()
该方法没有指定端口号,使用时系统会分配一个没有被占用的端口号,一般用于发送端。
②DatagramSocket(int port)
该方法指定了端口号,可以发送、接收。
③②DatagramSocket(int port, InetAddress addr)
该方法指定了端口号、ip地址,可以发送、接收,可用在多网卡的场景。
常用方法:void receive(DatagramPacket p)//将接收到的数据填充到DatagramPacket数据包,收到数据前会一直阻塞,当接收到数据时才会返回。 void send(DatagramPacket p)//用于发送DatagramPacket数据包,发送的数据要包含数据、长度、ip地址、端口号。 void close()//释放资源。
使用案例:
public class T_UDP {
public static void main(String[] args) {
new Receiver().start();
new Send().start();
}
}
//线程,负责接收
class Receiver extends Thread{
@Override
public void run() {
try {
DatagramSocket ds = new DatagramSocket(8002);
DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
//一直接收
while (true){
ds.receive(dp);
byte[] arr = dp.getData();//创建byte数据接收数据
int len = dp.getLength(); //获取数据长度
String ip = dp.getAddress().getHostAddress();//获取IP地址
System.out.println("ip:"+ip+new String(arr,0, len));//显示数据
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//发送端
class Send extends Thread{
@Override
public void run() {
try {
DatagramSocket ds = new DatagramSocket();//实例化DatagramSocket
Scanner sc = new Scanner(System.in);//输入扫描器
while (true){
String str = sc.nextLine();
if("quit".equals(str)){//遇到quit字符串退出
break;
}
DatagramPacket dp = new DatagramPacket(str.getBytes(),str.getBytes().length,InetAddress.getByName("localhost"),8002);//发送数据
System.out.println("发送数据");
System.out.println(dp.getAddress());
ds.send(dp);
}
ds.close();//释放资源
} catch (SocketException | UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
三、UDP与TCP的差异
UDP只有发送端与接收端,不区分客户端与服务端,相互之间可以任意的发送数据。而TCP是严格区分客户端与服务端的,通信时,必须由客户端去连接服务端才能实现通信,服务端不能主动连接客户端,且服务端需要先运行,等待客户端的连接。