Socket 套接字,是由系统提供用于网络通信的技术,是基于TCP/IP 协议的网络通信的基本操作单元。基于 Socket套接字的网络程序开发就是网络编程。
流套接字:使用传输层 TCP 协议
TCP,即 Transmission Control Protol (传输控制协议),传输层协议。
特点:
• 有连接(通信双方需要刻意保留对方的信息):打电话就是有连接,要想打电话,得先有对方的号码。
• 可靠传输
• 面向字节流:以字节为传输的基本单位,读写非常灵活
• 由接收缓冲区,也有发送缓冲区
• 全双工:一条路径,双向通信。即两端可以同时收发消息。(半双工:一端发消息,另一端只能接收消息)。
• 大小不限
数据报套接字:使用传输层 UDP 协议
UDP,即 User Datagram Protocol (用户数据报协议),传输层协议。
特点:
• 无连接(通信双方不需要刻意保留对方的消息): 发短信,发送验证码时并不需要知道对方的号码,可以直接发送消息。
• 不可靠传输
• 面向数据报:以一个 UDP 数据报为基本单位。
• 有接收缓冲区
• 全双工
• 大小有限:一次最多传输64k
DatagramSocket 是UDP Socket, 用于发送和接收UDP数据报.
DatagramSocket 构造方法:
DatagramSocket 方法:
DatagramPacket API 是UDP Socket 发送和接收的数据报.
DatagramPacket 构造方法:
DatagramPacket 方法:
UDP 客户端
public class UDPEchoClient {
private String SeverIP;
private int SeverPort;
private DatagramSocket socket=null;
public UDPEchoClient(int SeverPort,String SeverIP) throws SocketException {
socket=new DatagramSocket();
this.SeverPort=SeverPort;
this.SeverIP=SeverIP;
}
public void start() throws IOException {
Scanner scanner=new Scanner(System.in);
while (true){
System.out.print("->");
String request=scanner.next();
DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length,
InetAddress.getByName(SeverIP),SeverPort);
socket.send(requestPacket);
DatagramPacket responsePacket=new DatagramPacket(new byte[4096],4096);
socket.receive(responsePacket);
String response=new String(responsePacket.getData(),0,responsePacket.getLength());
System.out.printf("req: %s,resp: %s\n",request,response);
}
}
public static void main(String[] args) throws IOException {
// UDPEchoClient udpEchoClient=new UDPEchoClient(9090,"127.0.0.1");
UDPEchoClient udpEchoClient=new UDPEchoClient(9090,"114.115.149.19");
udpEchoClient.start();
}
}
UDP 服务器端
public class UDPEchoSever {
//定义一个socket 对象,通过网络通信,必须要使用socket 对象.
private DatagramSocket socket=null;
//绑定一个端口,不一定能成功
//如果某个端口已经被别的进程占用了,此时这里的绑定操作就会出错
//同一个主机上,一个端口,同一时刻,只能被一个进程绑定
public UDPEchoSever(int port) throws SocketException {
socket=new DatagramSocket(port);
}
//启动服务器
public void start() throws IOException {
System.out.println("服务器启动");
while (true) {
//每次循环做三件事:
//1.读取请求并解析,构造空饭盒(构造DatagramPacket对象获取数据报)
DatagramPacket requestSocket=new DatagramPacket(new byte[4096],4096);
//食堂大妈给饭盒里盛饭(将数据报中的内容全部放进上述构造的DatagramPacket对象中)
socket.receive(requestSocket);
//为了方便处理这个请求,把数据报转成String
String request=new String(requestSocket.getData(),0, requestSocket.getLength());
//2.根据请求计算响应(此处省略了这个步骤,没有明确的业务逻辑,直接返回了原内容)
String response=process(request);
//3.把响应结果写回到客户端
// 格局response 字符串,构造一个DatagramPacket
// 和请求 packet 不同,此处构造响应的时候,需要指定这个包发给谁
DatagramPacket responseSocket=new DatagramPacket(response.getBytes(),response.getBytes().length,requestSocket.getSocketAddress());
socket.send(responseSocket);
System.out.printf("[%s:%d]:req: %s,resp: %s\n",requestSocket.getAddress().toString(),requestSocket.getPort(),request,response);
}
}
//这个方法希望根据请求计算响应,如果后续写个别的服务器,不再回显了,而是有具体的业务了,就可以修改 process方法,根据需要重新构造响应.
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
UDPEchoSever udpEchoSever=new UDPEchoSever(9090);
udpEchoSever.start();
}
}
简单的单词查询服务器
public class UdpDictServer extends UDPEchoSever{
private Map map=new HashMap<>();
public UdpDictServer(int port) throws SocketException {
super(port);
map.put("小猫","cat");
map.put("小狗","dog");
map.put("小猪","pig");
}
@Override
public String process(String request) {
return map.getOrDefault(request,"不包含要查找的单词!!");
}
public static void main(String[] args) throws IOException {
UdpDictServer udpDictServer=new UdpDictServer(9090);
udpDictServer.start();
}
}
ServerSocket 是创建TCP服务端Socket 的API.
Socket 是客户端Socket, 或服务端中接收到客户端建立连接(accept 方法)的请求后,返回的服务端Socket.不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,即用来与对方收发数据的.
服务器端:
package demo;
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;
/**
* Created with IntelliJ IDEA.
* Description:
* User: zhao3
* Date: 2023-11-02
* Time: 11:07
*/
public class TCPEchoServer {
ServerSocket socket=null;
public TCPEchoServer(int port) throws IOException {
socket=new ServerSocket(port);
}
public void start() throws IOException {
//无法同时处理多个客户的请求:如果直接调用,processConnection方法会影响这个循环的二次执行,导致 accept不及时. 法
/*while (true){
Socket clientSocket=socket.accept();
processConnection(clientSocket);
}*/
//创建线程池,可以同时处理多个用户的请求
ExecutorService pool= Executors.newCachedThreadPool();
while (true){
Socket clientSocket=socket.accept();
pool.submit(new Runnable() {
@Override
public void run() {
try {
processConnection(clientSocket);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
}
}
private void processConnection(Socket clientSocket) throws IOException {
try (InputStream inputStream=clientSocket.getInputStream();
OutputStream outputStream=clientSocket.getOutputStream()){
//套上一个套接字,将字节流改为字符流
PrintWriter printWriter=new PrintWriter(outputStream);
Scanner scanner=new Scanner(inputStream);
while (true){
//将字节流转换成字符流,每个请求用'\n'来 分割
if(!scanner.hasNext()){
//请求结束
break;
}
String request=scanner.next();
String response=process(request);
printWriter.println(response);
printWriter.flush();
System.out.printf("[%s,%d]:req:%s,res:%s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,response);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
clientSocket.close();
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TCPEchoServer tcpEchoServer=new TCPEchoServer(9090);
tcpEchoServer.start();
}
}
客户端:
package demo;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
/**
* Created with IntelliJ IDEA.
* Description:
* User: zhao3
* Date: 2023-11-02
* Time: 11:07
*/
public class TCPEchoClient {
Socket socket=null;
public TCPEchoClient(String post, int port) throws IOException {
socket=new Socket(post,port);
}
public void start() throws IOException {
Scanner scanner=new Scanner(System.in);
try (InputStream inputStream=socket.getInputStream();
OutputStream outputStream= socket.getOutputStream()){
Scanner scannerFromSocket=new Scanner(inputStream);
PrintWriter printWriter=new PrintWriter(outputStream);
while (true){
System.out.print("->");
String request=scanner.next();
printWriter.println(request);
printWriter.flush();
String response= scannerFromSocket.next();
System.out.printf("req:%s,res:%s\n",request,response);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
socket.close();
}
}
public static void main(String[] args) throws IOException {
TCPEchoClient tcpEchoClient=new TCPEchoClient("127.0.0.1",9090);
tcpEchoClient.start();
}
}