1、什么是TCP协议呢?
1.TCP是一种面向连接,安全、可靠的传输数据的协议。
2.传输前,采用“三次握手”方式,点对点通信,是可靠的 。
3.在连接中可进行大数据量的传输。
注意: 如果在java中只要是使用java.net.Socket类实现通信,底层即是使用了TCP协议。
Socket
构造器 |
说明 |
public Socket(String host , int port) |
创建发送端的Socket对象与服务端连接,参数为服务端程序的ip和端口。 |
Socket类成员方法
方法 |
说明 |
OutputStream getOutputStream() |
获得字节输出流对象(发) |
InputStream getInputStream() |
获得字节输入流对象(收) |
需求:服务端实现
实现步骤:
1.创建ServerSocket对象,注册服务端端口。
2.调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。
3.通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。
4.释放资源:关闭socket管道
ServerSocket代码实现服务端:
服务端代码:
public static void main(String[] args) {
try {
System.out.println("===========服务端启动了==========");
//1、注册端口
ServerSocket serverSocket = new ServerSocket(7777);
//2、必须调用accept方法,等待接受用户的Socket链接请求,建立Socket通信管道
Socket socket = serverSocket.accept();
//3、从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
//4、把字节输入流包装缓冲字符输入流进行消息接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//5、按照行读取消息
String msg;
if ((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress()+"说了:"+msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
需求:客户端实现
实现步骤:
1.创建客户端的Socket对象,请求与服务端的连接。
2.使用socket对象调用getOutputStream()方法得到字节输出流。
3.使用字节输出流完成数据的发送。
4.释放资源:关闭socket管道。
客户端代码:
public static void main(String[] args) {
try {
System.out.println("==========客户端启动了============");
//1、创建Socket通信管道请求与服务端链接
//public Socket (String host , int port)
//参数一:服务端的IP地址
//参数二:服务端的端口
Socket socket = new Socket("127.0.0.1",7777);
//2、从socket通信管道中得到一个字节输出流 负责发送数据
OutputStream os = socket.getOutputStream();
//3、把低级的字节流打包成打印流
PrintStream ps = new PrintStream(os);
//4、发送消息
ps.println("我是TCP的客户端,我已经与你对接,并发出邀请:约吗?");
ps.flush();
//关闭资源(不建议关闭)
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
实现结果:
注意:一定要服务端先启动,客户端才可以启动,否则就会报错。
ServerSocket(服务端)
构造器 |
说明 |
public ServerSocket(int port) |
注册服务端端口 |
ServerSocket类成员方法
方法 |
说明 |
public Socket accept() |
等待接收客户端的Socket通信连接 连接成功返回Socket对象与客户端建立端到端通信 |
注意:如果客户端和服务端一方出了问题,另一方也会失效或者出错。
需求:使用TCP通信实现:多发多收消息、
实现步骤:
1.可以使用死循环控制服务端收完消息继续等待接收下一个消息。
2.客户端也可以使用死循环等待用户不断输入消息。
3.客户端一旦输入了exit,则关闭客户端程序,并释放资源。
服务端代码:
public static void main(String[] args) {
try {
System.out.println("===========服务端启动了==========");
//1、注册端口
ServerSocket serverSocket = new ServerSocket(7777);
//2、必须调用accept方法,等待接受用户的Socket链接请求,建立Socket通信管道
Socket socket = serverSocket.accept();
//3、从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
//4、把字节输入流包装缓冲字符输入流进行消息接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//5、按照行读取消息
String msg;
while ((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress()+"说了:"+msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
客户端代码:
public static void main(String[] args) {
try {
System.out.println("==========客户端启动了============");
//1、创建Socket通信管道请求与服务端链接
//public Socket (String host , int port)
//参数一:服务端的IP地址
//参数二:服务端的端口
Socket socket = new Socket("127.0.0.1",7777);
//2、从socket通信管道中得到一个字节输出流 负责发送数据
OutputStream os = socket.getOutputStream();
//3、把低级的字节流打包成打印流
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String msg = sc.nextLine();
//4、发送消息
ps.println(msg);
ps.flush();
}
//关闭资源(不建议关闭)
// socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
实现结果:
这次多发多收是如何实现的呢?
通过客户端使用循环反复地发送消息,服务端使用循环反复地接收消息。从而实现多发多收。
注意:目前服务端是单线程的,每次只能处理一个客户端的消息。
使用多线程——同时接受多个客户端消息(优化多发多收)
客户端代码:
package com.wfl.d7_socket3;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
/**
目标: 多发多收
*/
public class ClientDemo1 {
public static void main(String[] args) {
try {
System.out.println("==========客户端启动了============");
//1、创建Socket通信管道请求与服务端链接
//public Socket (String host , int port)
//参数一:服务端的IP地址
//参数二:服务端的端口
Socket socket = new Socket("127.0.0.1",7777);
//2、从socket通信管道中得到一个字节输出流 负责发送数据
OutputStream os = socket.getOutputStream();
//3、把低级的字节流打包成打印流
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String msg = sc.nextLine();
//4、发送消息
ps.println(msg);
ps.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
服务端启动代码:
package com.wfl.d7_socket3;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
目标: 实现服务端可以同时处理多个客户端消息
*/
public class ServerDemo2 {
public static void main(String[] args) {
try {
System.out.println("===========服务端启动了==========");
//1、注册端口
ServerSocket serverSocket = new ServerSocket(7777);
while (true) {
//2、必须调用accept方法,等待接受用户的Socket链接请求,建立Socket通信管道
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress()+"上线了~~");
//3、开始创建独立的线程处理socket
new ServerReaderThread(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
服务端线程代码:
package com.wfl.d7_socket3;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread (Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
//3、从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
//4、把字节输入流包装缓冲字符输入流进行消息接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//5、按照行读取消息
String msg;
while ((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress()+"说了:"+msg);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress()+"下线了~~");
}
}
}
重点:
1.主线程定义了循环负责接收客户端Socket管道连接
2.每接收到一个Socket通信管道后分配一个独立的线程负责处理它。
使用线程池优化(引入线程池)
客户端代码:
package com.wfl.d8_socket4;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
/**
拓展: 使用线程池优化:实现通信。
*/
public class ClientDemo1 {
public static void main(String[] args) {
try {
System.out.println("==========客户端启动了============");
//1、创建Socket通信管道请求与服务端链接
//public Socket (String host , int port)
//参数一:服务端的IP地址
//参数二:服务端的端口
Socket socket = new Socket("127.0.0.1",7777);
//2、从socket通信管道中得到一个字节输出流 负责发送数据
OutputStream os = socket.getOutputStream();
//3、把低级的字节流打包成打印流
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String msg = sc.nextLine();
//4、发送消息
ps.println(msg);
ps.flush();
}
//关闭资源(不建议关闭)
// socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
服务端代码:采用线程池。
package com.wfl.d8_socket4;
import com.wfl.d7_socket3.ServerReaderThread;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;
/**
目标: 实现服务端可以同时处理多个客户端消息
*/
public class ServerDemo2 {
//使用静态变量记住一个线程池对象
private static ExecutorService pool = new ThreadPoolExecutor(3,
5,6, TimeUnit.SECONDS,new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) {
try {
System.out.println("===========服务端启动了==========");
//1、注册端口
ServerSocket serverSocket = new ServerSocket(7777);
while (true) {
//2、必须调用accept方法,等待接受用户的Socket链接请求,建立Socket通信管道
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress()+"上线了~~");
//任务对象负责读取消息
Runnable srr = new ServerReaderRunnable(socket);
pool.execute(srr);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.wfl.d8_socket4;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class ServerReaderRunnable implements Runnable {
private Socket socket;
public ServerReaderRunnable(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
//3、从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
//4、把字节输入流包装缓冲字符输入流进行消息接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//5、按照行读取消息
String msg;
while ((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress()+"说了:"+msg);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress()+"下线了~~");
}
}
}
使用线程池的优势:
服务端可以复用线程处理多个客户端,可以避免系统瘫痪。
适合客户端通信时长较短的场景。
即时通信是啥?
即时通信,是指一个客户端的消息发出去,其他客户端可以接收到。
要咋设计?
1.即时通信需要进行端口转发的设计思想。
2.服务端需要把在线的Socket管道存储起来
3.一旦收到一个消息要推送给其他管道
实例:微信群发,就是即时通信的思想!
TCP通信如何实现BS请求网页信息回来呢?
1.客户端使用浏览器发起请求(不需要开发客户端)
2.服务端必须按照浏览器的协议规则响应数据。