文章目录
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);
}
@Override
protected 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");
}
@Override
protected 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);
}
@Override
public 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);
}
@Override
public 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();
}
}