UDP:
准备工作:udp套接字认识
DatagramSocket:
DatagramSocket是Java中用于实现UDP协议的套接字类。它提供了在网络上发送和接收UDP数据报的功能。
方法:
DatagramPacket
DatagramPacket是Java中用于表示UDP数据报的类。它封装了一个数据报(Datagram)以及该数据报的目标地址、目标端口号等信息。通过DatagramPacket可以实现在网络上发送和接收UDP数据报。
方法:
另外客户端在输入ip时要构建成一个InetAddress对象来表示ip
这里只介绍了实现回显程序所需要的方法。
实现回显程序:
实现服务端:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class Sever2 {
private DatagramSocket datagramSocket;
public Sever2(int port) throws SocketException {
//服务器端口号需要固定,这里指定服务器端口号
datagramSocket = new DatagramSocket(port);
}
//处理请求
//这里我们是一个回显程序,所以不进行处理,直接返回
public String process(String s) {
return s;
}
public void start() throws IOException {
System.out.println("服务器启动");
while(true) {
//构建请求数据报
DatagramPacket requestPacket = new DatagramPacket(new byte[2000],2000);
//接收客户端的请求数据报
//如果请求还没有接收到,就一直阻塞等待
datagramSocket.receive(requestPacket);
//转化成字符串并进行处理
//这样的转字符串的前提是, 后续客户端发的数据就是一个文本的字符串.
String request = new String(requestPacket.getData(),0,requestPacket.getLength());
//处理请求
String respond = process(request);
//处理完毕,构建返回数据报
//从请求数据报中获取客户端的端口和ip地址
DatagramPacket respondPacket =
new DatagramPacket(respond.getBytes(),respond.getBytes().length,requestPacket.getSocketAddress());
//向客户端返回数据报
datagramSocket.send(respondPacket);
System.out.printf("[%s:%d] req: %s, resp: %s\n", respondPacket.getAddress().toString(), respondPacket.getPort(),
request, respond);
}
}
public static void main(String[] args) throws IOException {
Sever2 sever2 = new Sever2(2030);
sever2.start();
}
}
实现客户端
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
public class Client2 {
//服务器端口号
private int port;
//服务器ip:
private String ip;
private DatagramSocket datagramSocket;
public Client2(int port, String ip) throws SocketException {
this.port = port;
this.ip = ip;
//创建套接字对象:客户端端口由系统随机分配
datagramSocket = new DatagramSocket();
}
public void start() throws IOException {
Scanner scan = new Scanner(System.in);
System.out.println("客户端启动");
while(true) {
//提示用户输入数据
System.out.println("->");
String requet = scan.next();
//构建udp发送数据报
DatagramPacket requestPacket =
new DatagramPacket(requet.getBytes(),requet.getBytes().length, InetAddress.getByName(ip),port);
//向服务器发送待处理数据报
datagramSocket.send(requestPacket);
//构建啊udp接收数据报
DatagramPacket reponsePacket = new DatagramPacket(new byte[2000] , 2000);
//这里reponsePacket是一个接收型参数
datagramSocket.receive(reponsePacket);
//利用String构造方法将数据报转化成一个字符串
String reponse = new String(reponsePacket.getData(),0,reponsePacket.getLength());
System.out.println(reponse);
}
}
public static void main(String[] args) throws IOException {
//指定服务器端口,ip地址为环回地址
Client2 client2 = new Client2(2030,"127.0.0.1");
client2.start();
}
}
效果:
TCP:
准备工作:tcp套接字认识
ServerSocket
Socket
ServerSocket是服务器端套接字,用于监听客户端的连接请求。一旦有客户端连接请求到达,ServerSocket就会通过accept方法来获取到这个请求并创建一个Socket对象进行描述。然后决定是否建立连接。
Socket是客户端套接字,用于与服务器进行通信。一旦连接建立成功,服务器和客户端就可以利用Socket通过输入输出流进行通信。
实现回显程序
实现服务端
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TcpSever2 {
//这里使用线程池来实现多个客户端共同访问服务器的并发场景
private ExecutorService pool = Executors.newCachedThreadPool();
private ServerSocket serverSocket = null;
//创建服务器端套接字并指定端口
public TcpSever2(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("服务器上线");
while(true) {
//监听客户端的连接请求,并通过accept方法获取到这个请求,并创建一个Socket来描述这个请求
Socket socket = serverSocket.accept();
//processConnection(socket);
//线程池来处理并发的连接请求
pool.submit(new Runnable() {
@Override
public void run() {
processConnection(socket);
}
});
}
}
//请求的处理
public void processConnection(Socket socket) {
//打印客户端信息
System.out.printf("客户端:[ip:%s|port:%d]\n",socket.getInetAddress().toString(),socket.getPort());
//通过流对象进行通信
try(InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream()) {
while(true) {
//获取流对象信息
Scanner scan = new Scanner(inputStream);
if(!scan.hasNext()) {
System.out.println("客户端下线");
System.out.printf("客户端:[ip:%s|port:%d]\n",socket.getInetAddress().toString(),socket.getPort());
}
String request = scan.next();
//按照程序逻辑处理请求信息,这里由于是回显程序,所以直接返回即可
String respond = process(request);
//将处理好的信息输入到流对象中
PrintWriter printWriter = new PrintWriter(outputStream);
printWriter.println(respond);
//为什么进行flush
//pritWriter内置了一个缓冲区
//flush确保缓冲区中的所有数据都被写入到输出流中,
printWriter.flush();
System.out.printf("[ip:%s|port:%d],req:%s,rep:%s\n",socket.getInetAddress().toString(),socket.getPort(),request,respond);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//高并发场景,可能会建立很多个socket,如果不关闭就会造成文件资源泄露
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TcpSever2 tcpSever2 = new TcpSever2(8086);
tcpSever2.start();
}
}
实现客户端
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class TcpClint2 {
private Socket socket = null;
//指定socket发起连接的目标的ip和端口
public TcpClint2(String ip,int port) throws IOException {
socket = new Socket(ip , port);
}
public void start() {
Scanner scan = new Scanner(System.in);
System.out.println("客户端上线");
try(InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream()){
while(true){
//控制台输入提示符
System.out.println(">");
String request = scan.next();
//将请求处理的信息输入到流对象当中
PrintWriter printWriter = new PrintWriter(outputStream);
printWriter.println(request);
//为什么进行flush
//pritWriter内置了一个缓冲区
//flush确保缓冲区中的所有数据都被写入到输出流中,
printWriter.flush();
//从流中获取处理完的信息
Scanner scanResponse = new Scanner(inputStream);
String response = scanResponse.next();
System.out.println(response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
TcpClint2 tcpClint2 = new TcpClint2("127.0.0.1",8086);
tcpClint2.start();
}
}
效果:
注意的点:
1.printWriter像流中输出了信息后,最好调用flush方法刷新一下,确保缓冲区中的所有数据都被写入到输出流中。
2.客户端与服务器端相互配合:比如:客户端在写入请求时,使用println,服务器端在读取请求时就用next();
3.并发场景下注意一定要关闭涉及到打开文件的操作,比如这里的Socket,流对象,以防止发生文件资源泄漏的情况