目录
一. Socket 编程
二. 基于Udp的回显程序
1.服务器端
2. 客户端
三. 基于Tcp的回显程序
1. 服务器端
2. 客户端
Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程;
java.net 包中提供了两种常见的网络协议的支持:
TCP:TCP(英语:Transmission Control Protocol,传输控制协议) 是一种面向连接的、可靠的、基于字节流的传输层通信协议,TCP 层是位于 IP 层之上,应用层之下的中间层。TCP 保障了两个应用程序之间的可靠通信。通常用于互联网协议,被称 TCP / IP。
UDP:UDP (英语:User Datagram Protocol,用户数据报协议),位于 OSI 模型的传输层,一个无连接的协议,提供了应用程序之间要发送数据的数据报。由于UDP缺乏可靠性且属于无连接协议,所以应用程序通常必须容许一些丢失、错误或重复的数据包。
public class UdpServer {
// 定义一个 soctet 对象
private DatagramSocket socket = null;
public UdpServer(int port) throws SocketException {
//构造 socket ,要绑定相关的端口号
socket = new DatagramSocket(port);
}
//启动服务器
public void start() throws IOException {
System.out.println("服务器启动!");
while (true) {
// 1.读取请求并解析
// 构造DatagramPacket包,用来接收
DatagramPacket requestPacket = new DatagramPacket(new byte[10000], 10000);
socket.receive(requestPacket);
//方便处理这个请求,把数据包转成String(这个操作不是非必须得)
String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
String response = process(request);
// 2.把响应结果写到客户端
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
response.getBytes().length,
requestPacket.getSocketAddress());
socket.send(responsePacket);
System.out.printf("[%s:%d] req: %s,resp: %s\n", requestPacket.getAddress().toString(),
requestPacket.getPort(), request, response);
}
}
public String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
UdpServer udpServer = new UdpServer(9090);
udpServer.start();
}
}
public class UdpClient {
private DatagramSocket socket = null;
private String serverIP;
private int serverPort;
public UdpClient(String serverIP, int serverPort) throws SocketException {
// 客户端不需要显示关联端口,系统自动分配空闲端口
socket = new DatagramSocket();
this.serverIP = serverIP;
this.serverPort = serverPort;
}
Scanner scanner = new Scanner(System.in);
// 客户端和服务器进行多次交互
public void start() throws IOException {
while (true) {
// 1.先从控制读取字符串
System.out.println("请输入: ");
String request = scanner.next();
// 2.把字符串构造DatagramPacket包,用来存储要发送的信息
DatagramPacket datagramPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
InetAddress.getByName(serverIP), serverPort);
socket.send(datagramPacket);
// 3.客户端读取服务器返回的响应
DatagramPacket responsePacket = new DatagramPacket(new byte[10000], 10000);
socket.receive(responsePacket);
// 4.把响应数据转化成String显示出来
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 {
UdpClient udpClient = new UdpClient("127.0.0.1", 9090);
udpClient.start();
}
}
步骤:
1.服务器先启动, 等待客户端发送数据,执行到receive,此时服务端进入一个阻塞状态;
2.客户端开始输入操作,并进行send;
3.客户端send之后,走到receive读取响应,会阻塞等待;同时服务器这边就从receive返回,走到process生成响应,再走到send;
4. 客户端收到服务器端send之后发来的数据后,会解除阻塞;服务器进入下一轮循环,走到receive,等待下一个请求;
public class TcpServer {
private ServerSocket serverSocket = null;
public TcpServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
public void start() throws IOException {
ExecutorService executorService = Executors.newCachedThreadPool();
System.out.println("服务器启动!");
while (true) {
Socket clientSocket = serverSocket.accept();
//写法1
// 每次来一个新的客户端,都创建一个心得线程
/*Thread thread = new Thread(()->{
try {
processConnect(clientSocket);
} catch (IOException e) {
e.printStackTrace();
}
});
thread.start();*/
//写法2,使用线程池
executorService.submit(new Runnable() {
@Override
public void run() {
try {
processConnect(clientSocket);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
private void processConnect(Socket clientSocket) throws IOException {
System.out.printf("[%s:%d] req: 客户端上线\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
try (InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream()) {
Scanner scanner = new Scanner(inputStream);
PrintWriter printWriter = new PrintWriter(outputStream);
while (true) {
// 1.读取请求
if (!scanner.hasNext()) {
// 读取的流到了结尾了
System.out.printf("[%s:%d] 客户端下线!", clientSocket.getInetAddress().toString(),clientSocket.getPort());
break;
}
String request = scanner.next();
// 2.根据请求计算响应
String response = process(request);
// 3.把响应写回到客户端
printWriter.println(response);
System.out.printf("[%s:%d] req: %s;resp: %s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort()
,request,response);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
clientSocket.close();
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TcpServer tcpServer = new TcpServer(9090);
tcpServer.start();
}
}
public class TcpClient {
private Socket socket = null;
public TcpClient(String serverIP, int port) throws IOException {
//服务器和客户端建立连接
socket = new Socket(serverIP, port);
}
public void start() {
Scanner scanner = new Scanner(System.in);
try (OutputStream outputStream = socket.getOutputStream();
InputStream inputStream = socket.getInputStream()) {
PrintWriter printWriter = new PrintWriter(outputStream);
Scanner scannerFromSocket = new Scanner(inputStream);
while (true) {
// 1.从键盘上读取用户输入的内容
System.out.println("请输入:");
String request = scanner.next();
// 2.读取的内容构造成请求,发送给服务器
//这里只是把数据写到内存的缓存区,等到缓存区满了,才会写到网卡
printWriter.println(request);
//手动刷新缓冲区
printWriter.flush();
// 3.从服务器读取响应内容
String response = scannerFromSocket.next();
// 4.把响应结果显示到控制台上
System.out.printf("req: %s;resp:%s\n", request, response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
TcpClient tcpClient = new TcpClient("127.0.0.1",9090);
tcpClient.start();
}
}
步骤:
1. 服务器先执行strat方法,执行到accept这里进入阻塞状态,等待客户端建立连接
2.客户端启动,和服务器建立连接,连接成功之后,服务器的accept就会返回;
3.服务器尝试从客户端读取请求,此时会进入阻塞状态
4.客户端从控制台读取用户输入,输入完之后,客户端开始发送请求出去,同时读取服务器响应,进入阻塞状态;
5.服务器收到客户端的请求之后,把响应写回到客户端;同时客户端收到服务器的响应,就可以处理数据了;
6.双方都进入下一次循环