赞
踩
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.服务端必须按照浏览器的协议规则响应数据。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。