网络编程
网络编程指的是编写跨多个设备(计算机)执行的程序,设备使用网络彼此连接
J2SE API的java.net包中包含一组类和接口,它们提供低级别的通信详细信息,开发者可编写专注于解决手头问题的程序。java.net包提供对两种常见网络协议的支持
-
TCP - TCP代表传输控制协议,它允许两个应用程序之间的可靠通信。TCP通常用于Internet协议,称为TCP/IP
-
UDP - UDP代表用户数据报协议,这是一种无连接协议,允许在应用程序之间传输数据包
Socket 编程
简介
Socket(套接字)是使用TCP提供通信机制在两台计算机之间进行通信。客户端程序在通信的末尾创建一个套接字,并尝试将套接字连接到服务器。建立连接后,服务器会在通信结束时创建一个套接字对象。客户端和服务器现在可以通过写入和读取套接字进行通信
java.net.Socket类表示一个套接字,java.net.ServerSocket类为服务器程序提供了一种监听客户端并与它们建立连接的机制。使用Socket(套接字)在两台计算机之间建立TCP连接时,会发生以下步骤
-
服务器实例化ServerSocket对象,表示要在哪个端口号上进行通信
-
服务器调用ServerSocket类的accept()方法。此方法等待,直到客户端连接到给定端口上的服务器
-
在服务器等待之后,客户端实例化Socket对象,指定要连接的服务器名称(IP地址)和端口号
-
Socket类的构造函数尝试将客户端连接到指定的服务器和端口号。如果建立了通信,则客户端现在具有能够与服务器通信的Socket对象
-
在服务器端,accept()方法返回对连接到客户端套接字的服务器上的新套接字的引用
建立连接后,可以使用I/O流进行通信。每个套接字都有一个OutputStream和一个InputStream。客户端的OutputStream连接到服务器的InputStream,客户端的InputStream连接到服务器的OutputStream
TCP是双端通信协议,因此可以同时跨两个流发送数据
Socket通信入门案例
搭建简单的Socket通信入门案例,通过一次性的客户端发送和服务端接收数据,掌握并了解
-
Socket服务端和客户端的基本编程
-
传输编码统一指定,防止乱码
服务端监听一个端口,等待连接的到来
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
/**
* @author 杨文成
* @date 2022/05/05 21:52
* @describe 服务端监听一个端口,等待连接的到来
*/
public class SocketServer {
public static void main(String[] args) throws IOException {
//服务端监听指定端口
ServerSocket serverSocket = new ServerSocket(8888);
//服务端阻塞等待连接
System.out.println("服务端阻塞等待连接端口8888……");
Socket socket = serverSocket.accept();
// 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len;
StringBuilder builder = new StringBuilder();
while ((len = inputStream.read(bytes)) != -1) {
//指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
builder.append(new String(bytes,0,len, StandardCharsets.UTF_8));
}
System.out.println("服务端接收到客户端的发来的信息:"+builder.toString());
//依次关闭资源
inputStream.close();
socket.close();
serverSocket.close();
}
}
客户端指定ip(域名亦可)和端口,连接到指定的服务端,进行数据传输
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
/**
* @author 杨文成
* @date 2022/05/05 21:59
* @describe 客户端指定ip和端口,连接到指定的服务端,进行数据传输
*/
public class SocketClient {
public static void main(String[] args) throws IOException {
//客户端指定ip(域名亦可)和端口,与服务端建立连接
Socket socket = new Socket("127.0.0.1", 8888);
//获取输出流
OutputStream outputStream = socket.getOutputStream();
//输出数据
outputStream.write("hello server".getBytes(StandardCharsets.UTF_8));
//依次关闭资源
outputStream.close();
socket.close();
}
}
测试时需要先启动服务端再启动客户端
入门案例优化
双向通信
服务端添加返回响应数据的操作
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
/**
* @author 杨文成
* @date 2022/05/05 21:52
* @describe 服务端监听一个端口,等待连接的到来
*/
public class SocketServer {
public static void main(String[] args) throws IOException {
//服务端监听指定端口
ServerSocket serverSocket = new ServerSocket(8888);
//服务端阻塞等待连接
System.out.println("服务端阻塞等待连接端口8888……");
Socket socket = serverSocket.accept();
// 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len;
StringBuilder builder = new StringBuilder();
while ((len = inputStream.read(bytes)) != -1) {
//指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
builder.append(new String(bytes,0,len, StandardCharsets.UTF_8));
}
System.out.println("server get message from client: "+builder.toString());
//服务端返回响应的数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello client,i get the message".getBytes(StandardCharsets.UTF_8));
//依次关闭资源
outputStream.close();
inputStream.close();
socket.close();
serverSocket.close();
}
}
客户端添加接收服务端返回数据的操作
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
/**
* @author 杨文成
* @date 2022/05/05 21:59
* @describe 客户端指定ip和端口,连接到指定的服务端,进行数据传输
*/
public class SocketClient {
public static void main(String[] args) throws IOException {
//客户端指定ip(域名亦可)和端口,与服务端建立连接
Socket socket = new Socket("127.0.0.1", 8888);
//获取输出流
OutputStream outputStream = socket.getOutputStream();
//输出数据
outputStream.write("hello server".getBytes(StandardCharsets.UTF_8));
//通过shutdownOutput告诉服务端,客户端已发送完数据,后续只能接受数据
socket.shutdownOutput();
//接收服务端返回的数据
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len;
StringBuilder builder = new StringBuilder();
while ((len = inputStream.read(bytes)) != -1) {
//指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
builder.append(new String(bytes,0,len,StandardCharsets.UTF_8));
}
System.out.println("client get message from server: " + builder.toString());
//依次关闭资源
inputStream.close();
outputStream.close();
socket.close();
}
}
服务端并发处理
在上面的例子中,服务端仅仅只是接受了一个Socket请求,并处理了它,然后就结束了,但是在实际开发中,一个Socket服务往往需要服务大量的Socket请求
循环接受请求并处理 -- 不建议
缺点:循环处理多个Socket请求,不过当一个请求的处理比较耗时的时候,后面的请求将被阻塞
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
/**
* @author 杨文成
* @date 2022/05/05 21:52
* @describe 服务端监听一个端口,等待连接的到来
*/
public class SocketServer {
public static void main(String[] args) throws IOException {
//服务端监听指定端口
ServerSocket serverSocket = new ServerSocket(8888);
//服务端阻塞等待连接
System.out.println("服务端阻塞等待连接端口8888……");
while (true) {
Socket socket = serverSocket.accept();
// 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len;
StringBuilder builder = new StringBuilder();
while ((len = inputStream.read(bytes)) != -1) {
//指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
builder.append(new String(bytes,0,len, StandardCharsets.UTF_8));
}
System.out.println("server get message from client: "+builder.toString());
//服务端返回响应的数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello client,i get the message".getBytes(StandardCharsets.UTF_8));
//依次关闭资源
outputStream.close();
inputStream.close();
socket.close();
}
}
}
线程池接收请求并处理 -- 推荐
用多线程的方式来处理Socket,即每有一个Socket请求的时候,就创建一个线程来处理它。不过在实际生产中,创建的线程会交给线程池来处理,为了
-
线程复用,创建线程耗时,回收线程慢
-
防止短时间内高并发,指定线程池大小,超过数量将等待,方式短时间创建大量线程导致资源耗尽,服务挂掉
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author 杨文成
* @date 2022/05/05 21:52
* @describe 服务端监听一个端口,等待连接的到来
*/
public class SocketServer {
public static void main(String[] args) throws IOException {
//服务端监听指定端口
ServerSocket serverSocket = new ServerSocket(8888);
//设置一个读取的超时时间,当超过指定的时间后,还没有读到数据,就假定这个连接无用
//然后抛异常,捕获异常后关闭连接就可以了
serverSocket.setSoTimeout(10000);
//服务端阻塞等待连接
System.out.println("服务端阻塞等待连接端口8888……");
//如果使用多线程,那就需要线程池,防止并发过高时创建过多线程耗尽资源
ExecutorService threadPool = Executors.newFixedThreadPool(100);
while (true) {
Socket socket = serverSocket.accept();
Runnable runnable = () -> {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
// 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len;
StringBuilder builder = new StringBuilder();
while ((len = inputStream.read(bytes)) != -1) {
//指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
builder.append(new String(bytes, 0, len, StandardCharsets.UTF_8));
}
System.out.println(String.format("server get message from client: %s,服务处理线程: %s",builder.toString(),Thread.currentThread().getName()));
//服务端返回响应的数据
outputStream = socket.getOutputStream();
outputStream.write("hello client,i get the message".getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
e.printStackTrace();
} finally {
//依次关闭资源
release(inputStream,outputStream,socket);
}
};
threadPool.execute(runnable);
}
}
/**
* 释放资源
* @param inputStream
* @param outputStream
* @param socket
*/
public static void release(InputStream inputStream,OutputStream outputStream,Socket socket){
try {
if (inputStream != null) {
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
标签:java,Socket,--,编程,import,Java,连接,服务端,socket
From: https://www.cnblogs.com/52-IT-y/p/16607938.html