UDP数据报和TCP流套接字编程
文章目录
- UDP数据报套接字编程
- 1.DatagramSocket类
- 2.DatagramPacket类
- 3. InetSocketAddress类
- 构建服务端和客户端
- TCP流套接字编程
- 1. ServerSocket类
- 2.Socket类
- 构建服务端和客户端
- 扩展
- 对话形式
- 简易的字典
- 多线程实现
- 线程池实现
UDP数据报套接字编程
1.DatagramSocket类
DatagramSocket 是UDP Socket,⽤于发送和接收UDP数据报。
DatagramSocket构造方法:
DatagramSocket():创建⼀个UDP数据报套接字的Socket,绑定到本机任意⼀个随机端⼝(⼀般⽤于客户端)
DatagramSocket(int port):创建⼀个UDP数据报套接字的Socket,绑定到本机指定的端⼝(⼀般⽤于服务端)
DatagramSocket方法:
void receive(DatagramPacket p):
从此套接字接收数据报(如果没有接收到数据报,该⽅法会阻塞等待)
void send(DatagramPacket p):从此套接字发送数据报(不会阻塞等待,直接发送)
void close():关闭此数据报
2.DatagramPacket类
DatagramPacket构造方法:
DatagramPacket(byte[] buf, int length):
构造⼀个DatagramPacket以⽤来接收数据报,接收的数据保存在字节数组(第⼀个参数buf)中,接收指定⻓度(第⼆个参数length)
DatagramPacket(byte[] buf, int offset, int length,SocketAddress address):构造⼀个DatagramPacket以⽤来发送数据报,发送的
数据为字节数组(第⼀个参数buf)中,从0到指定⻓度(第⼆个参数length)。address指定⽬的主机的IP和端⼝号
DatagramPacket方法:
InetAddress getAddress():从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址
int getPort():从接收的数据报中,获取发送端主机的端⼝号;或从发送的数据报中,获取接收端主机端⼝号
byte[] getData():获取数据报中的数据
3. InetSocketAddress类
构造UDP发送的数据报时,需要传⼊ SocketAddress ,该对象可以使⽤ InetSocketAddress来创建。
InetSocketAddress ( SocketAddress 的⼦类 )构造⽅法:
InetSocketAddress(InetAddress addr, int port):创建一个Socket地址,包含IP地址和端口号
构建服务端和客户端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;public class UDPEchosever {//创建服务端DatagramSocket socket;public UDPEchosever(int post) throws SocketException {if(post<1024||post>65535){throw new RuntimeException("端口号不符合条件");}socket=new DatagramSocket(post);}public void start() throws IOException {System.out.println("服务器已启动");while (true){//接收用户发来的数据DatagramPacket requestPacket = new DatagramPacket(new byte[1024], 1024);// 2. 接收数据socket.receive(requestPacket);// 3. 解析接收到的数据String request = new String(requestPacket.getData(), 0,requestPacket.getLength(), "UTF-8");//接收响应String respose=count(request);//发送响应DatagramPacket resposeSock=new DatagramPacket(respose.getBytes(StandardCharsets.UTF_8),0,respose.length(),requestPacket.getSocketAddress());socket.send(resposeSock);System.out.printf("[%s:%d] request :%s,response=%s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,respose);}}public String count(String request){return request;}public static void main(String[] args) throws IOException {UDPEchosever udpEchosever=new UDPEchosever(8888);udpEchosever.start();}
}
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;public class UDPEchoclient {//创建客户端DatagramSocket socket;//定义端口号,IP地址String IP;int port;public UDPEchoclient (String IP,int port) throws SocketException {this.socket=new DatagramSocket();this.IP=IP;this.port=port;}public void start() throws IOException {System.out.println("客服端已启动");while (true){System.out.println("->");//发送内容Scanner scanner=new Scanner(System.in);String request=scanner.nextLine();if(request==null||request.isEmpty()){System.out.println("字符串不能为空");}//包装用户发送的内容//SocketAddress address = new InetSocketAddress(IP, port);DatagramPacket requestPacket = new DatagramPacket(request.getBytes(StandardCharsets.UTF_8),0, request.getBytes().length, new InetSocketAddress(IP, port));
// DatagramPacket datagramPacket=new DatagramPacket(request.getBytes(StandardCharsets.UTF_8),0,request.length(),
// new InetSocketAddress(IP,port));socket.send(requestPacket);//用DatagramPacket接收服务器的响应数据DatagramPacket revicesocket=new DatagramPacket(new byte[1024],1024);socket.receive(revicesocket);//解析发送来的数据String respose=new String(revicesocket.getData(),0, revicesocket.getLength(),"UTF-8");System.out.printf("request:%s,repost:%s",request,respose);}}public static void main(String[] args) throws IOException {//System.out.println("你好");UDPEchoclient udpEchoclient=new UDPEchoclient("127.0.0.1",8888);udpEchoclient.start();}
}
TCP流套接字编程
1. ServerSocket类
ServerSocket 构造⽅法:
ServerSokcet(int port):创建⼀个服务端流套接字Socket,并绑定到指定端⼝
ServerSocket方法:
Socket accept():开始监听指定端⼝(创建时绑定的端⼝),有客⼾端连接后,返回⼀个服务端Socket对象,并基于该Socket建⽴与客⼾端的连接,否则阻塞等待
void close():关闭此套接字
2.Socket类
Socket 是客⼾端Socket,或服务端中接收到客⼾端建⽴连接(accept⽅法)的请求后,返回的服务端Socket。
不管是客⼾端还是服务端Socket,都是双⽅建⽴连接以后,保存的对端信息,及⽤来与对⽅收发数据的。
Socket的构造方法:
Socket(String host, int port):创建⼀个客⼾端流套接字Socket,并与对应IP的主机
上,对应端⼝的进程建⽴连接
Socket方法:
InetAddress getInetAddress() 返回套接字所连接的地址
InputStream getInputStream() 返回此套接字的输⼊流
OutputStream getOutputStream() 返回此套接字的输出流
构建服务端和客户端
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;public class TCPEchoSever {ServerSocket socket;//定义客户端public TCPEchoSever(int pase) throws IOException {if(pase<1024||pase>65535){System.out.println("端口号不符合条件");}this.socket=new ServerSocket(pase);}public void start() throws IOException {System.out.println("服务器已启动,等待客服端连接");//循环接收连接需求while (true){Socket clientsocket=socket.accept();//创建一个方法去接收客服端的内容reviceports(clientsocket);}}public void reviceports(Socket clientsocket) throws IOException {System.out.printf("[%s %d] 客服端上线了\n",clientsocket.getInetAddress().toString(),clientsocket.getPort());//使用输出流和输入流进行接收try(InputStream inputStream= clientsocket.getInputStream();OutputStream outputStream= clientsocket.getOutputStream()){while (true) {Scanner scanner = new Scanner(inputStream);//嵌入输入流if (!scanner.hasNextLine()) {System.out.printf("[%s %d] 客服端下线了",clientsocket.getInetAddress().toString(),clientsocket.getPort());break;}//获取用户发来的内容String request = scanner.nextLine();//对内容进行计算响应String respose = Calculation(request);//把响应写入输出流PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(respose);//刷新缓冲区printWriter.flush();//打印System.out.printf("[%s:%d] request=%s,respose=%s\n", clientsocket.getInetAddress().toString(), clientsocket.getPort(), request, respose);}}}protected String Calculation(String respose){return respose;}public static void main(String[] args) throws IOException {TCPEchoSever tcpEchoSever=new TCPEchoSever(8888);tcpEchoSever.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 TCPEchoClient {//创建客服端Socket socket;String IP;int port;public TCPEchoClient(String IP, int port) throws IOException {if (IP == null || IP.isEmpty()) {throw new RuntimeException("IP不能为空");}if (port < 1024 || port > 65535) {throw new RuntimeException("端口号不符合要求");}socket = new Socket(IP, port);}public void start() {System.out.println("客服端已启动");try (InputStream inputStream = this.socket.getInputStream();OutputStream outputStream = this.socket.getOutputStream()) {while (true) {Scanner scanner = new Scanner(System.in);String request = scanner.nextLine();if (request.isEmpty() || request == null) {System.out.println("发送内容不能为空");}PrintWriter printWriter = new PrintWriter(outputStream);//把数据写入输出流printWriter.println(request);printWriter.flush();//接收服务端发送来的数据Scanner scan = new Scanner(inputStream);String respose = scan.nextLine();System.out.println("request:" + request + "repose:" + respose);}} catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws IOException {TCPEchoClient tcpEchoClient=new TCPEchoClient("127.0.0.1",8888);tcpEchoClient.start();}
}
扩展
连接的端口号自定义在1024-65535间,自己定义统一就行,都是在服务端修改,客服端不发生修改
对话形式
import java.io.IOException;
import java.util.Scanner;public class TCPEchoexchange extends TCPEchoSever {public TCPEchoexchange(int pase) throws IOException {super(pase);}@Overrideprotected String Calculation(String request) {//打印发送来的内容System.out.println(request);System.out.println("请用户输入回应内容");while (true){Scanner scanner=new Scanner(System.in);String respose=scanner.nextLine();if(respose==null||respose.isEmpty()){System.out.println("输入内容不能为空");}return respose;}}public static void main(String[] args) throws IOException {TCPEchoexchange tcpEchoexchange=new TCPEchoexchange(6666);tcpEchoexchange.start();}
}
简易的字典
import java.io.IOException;
import java.util.HashMap;public class TCPEchoMapsever extends TCPEchoSever{HashMap<String,String> map;public TCPEchoMapsever(int pase) throws IOException {super(pase);map=new HashMap<>();map.put("狗","dog");map.put("猫","cat");map.put("鸡","chicken");map.put("猪","pig");}@Overrideprotected String Calculation(String request) {String key= map.getOrDefault(request,"没有找到");return key;}public static void main(String[] args) throws IOException {TCPEchoMapsever tcpEchoMapsever=new TCPEchoMapsever(8888);tcpEchoMapsever.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 TCPchoThread extends TCPEchoSever{public TCPchoThread(int pase) throws IOException {super(pase);}@Overridepublic void start() throws IOException {System.out.println("服务端已启动");while (true){Socket client=socket.accept();Thread th = new Thread (()->{try {reviceports(client);} catch (IOException e) {throw new RuntimeException(e);}});th.start();}}public static void main(String[] args) throws IOException {TCPchoThread tcPchoThread=new TCPchoThread(9999);tcPchoThread.start();}
线程池实现
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class TCPEchoPoolthread extends TCPEchoSever {public TCPEchoPoolthread(int pase) throws IOException {super(pase);}@Overridepublic void start() throws IOException {ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(4,10,1,TimeUnit.SECONDS,new ArrayBlockingQueue<>(2));System.out.println("服务端已启动,等待客服端连接");while (true){Socket client=socket.accept();threadPoolExecutor.submit(()->{try {reviceports(client);} catch (IOException e) {throw new RuntimeException(e);}});}}public static void main(String[] args) throws IOException {TCPEchoPoolthread tcpEchoPoolthread=new TCPEchoPoolthread(7777);tcpEchoPoolthread.start();}
}