目录
TCP常用API
ServerSocket:专门给服务端使用的socket。
**Socket:**既可以提供给客户端使用,也可以给服务端使用。
ServerSocket
构造方法:
|------------------------|------------------------------|
| 方法签名 | 方法说明 |
| ServerSocket(int port) | 创建一个服务端嵌套字,并且指定服务端所占用的进程 |
成员方法 accept:
方法签名 | 方法说明 |
---|---|
Socket accept() | TCP是"有连接"的协议,TCP客户端与服务端一定要建立连接,才可以互相发送消息。因此这个accept方法,返回的socket对象,服务端就是通过这个socket对象和客户端进行通信的。 如果服务端没有收到socket对象,那么就会阻塞等待,无法进行通信。 |
Socket
对于服务端来说,是由accept()方法返回的的,返回的socket对象用于和客户端进行通信。
构造方法 :
对客户端来说,构造方法构造对象时需要指定ip地址和端口号,这个ip和端口号是服务端的
两个普通常用的方法:
|-------------------|------------------------|
| 方法签名 | 方法说明 |
| getInputStream() | 通过socket对象,获取到内部的输入流对象 |
| getOutputStream() | 通过socket对象,获取到内部的输出流对象 |
TCP服务端(单线程)
属性+构造方法:
需要在TcpEchoServer内部封装一个属性,这个属性是ServerSocket。
在构造方法当中,需要指定ServerSocket占用哪个端口号,此端口号就是服务端的端口号
代码编写
java
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;
public class TcpEchoServer {
/**
* 用于TCP客户端和服务端通信的socket对象
*/
private ServerSocket serverSocket;
/**
* 构造方法指定服务端所占用的端口号
*/
public TcpEchoServer(int port) throws IOException {
serverSocket=new ServerSocket(port);
}
/**
* 启动服务端
*/
public void start() throws IOException {
System.out.println("启动服务端!");
while(true){
//使用clientSocket与客户端交流
Socket clientSocket=serverSocket.accept();//1、接收客户端发送的socket
processConnection(clientSocket);//2、处理客户端的连接
}
}
/**
* 处理客户端发送来的连接
* 客户端发送来的连接:clientSocket
*/
public void processConnection(Socket clientSocket) throws IOException {
//输出客户端ip地址和端口号
System.out.println("客户端已上线!客户端的IP是:"+clientSocket.getInetAddress()+
"客户端的端口号是:"+clientSocket.getPort());
//2-1、读取clientSocket中的输入输出流对象
InputStream inputStream=clientSocket.getInputStream();
OutputStream outputStream=clientSocket.getOutputStream();
//使用while循环处理多个请求和响应
while (true){
//2-2、通过Scanner来获取输入流
Scanner scanner=new Scanner(inputStream);
//读取完毕直接返回
if(!scanner.hasNext()){
System.out.println("客户端已经下线!客户端的IP是:"
+clientSocket.getInetAddress()+
";客户端的端口是:"+clientSocket.getPort());
//退出循环
break;
}
//2-3、根据请求计算响应
String request=scanner.next();
//构造回写内容
String response="服务器已经响应:"+request;
//2-4、使用PrintWriter发送OutputStream
PrintWriter printWriter=new PrintWriter(outputStream);
printWriter.println(response);
//刷新缓冲区保证能够发送出去
printWriter.flush();
}
//2-5、关闭连接
clientSocket.close();
}
public static void main(String[] args) throws IOException {
TcpEchoServer tcpEchoServer=new TcpEchoServer(9090);
tcpEchoServer.start();//服务端启动
}
}
TCP客户端(单线程)
属性+构造方法
需要在TcpEchoClient中加入Socket指定服务端的Ip地址和端口号
java
import java.io.IOException;
import java.net.Socket;
public class TcpEchoClient {
//客户端属性socket指定服务端IP+端口号
private Socket socket;
public TcpEchoClient(String serverIp,int port) throws IOException {
socket=new Socket(serverIp,port);
}
}
TCP是有连接的,所以要想使客户端和服务端可以通信就需要先建立连接,这里的socket对象创建时就说明客户端和服务端成功建立了连接。
客户端的socket创建完成的瞬间,服务端的accept方法就会立即接收到客户端的socket对象。
代码编写
java
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class TcpEchoClient {
//客户端属性socket指定服务端IP+端口号
private Socket socket;
public TcpEchoClient(String serverIp,int port) throws IOException {
socket=new Socket(serverIp,port);
}
//启动客户端
public void start() throws IOException {
System.out.println("客户端已上线!");
//1、利用socket获取到与服务端进行数据交互的输入输出流(inputStream和outputStream)
Scanner input=new Scanner(System.in);
InputStream inputStream = socket.getInputStream();
OutputStream outputStream=socket.getOutputStream();//都是相对于客户端来进行输入/输出操作的。
while(true){
//2、从控制台获取用户输入的信息
System.out.println("输入你想要发送的信息:");
String request=input.next();
//3、将获取到的request通过流的方式发送给服务端
PrintWriter printWriter=new PrintWriter(outputStream);
printWriter.println(request);
printWriter.flush();//刷新缓冲区
//4、通过Scanner读取服务端响应并进行回显
//读取服务端响应
Scanner scanner=new Scanner(inputStream);
String response=scanner.next();
//响应内容回显到界面
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient tcpEchoClient=new TcpEchoClient("127.0.0.1",9090);
tcpEchoClient.start();//启动客户端
}
}
单线程TCP客户端---服务端通信结果
单线程TCP存在的问题
当服务端启动之后,如果有客户端与服务端建立连接,服务端的accept方法就会返回一个socket对象,之后再通过processConnection方法对该客户端的socket不断进行while循环进行处理,调用scanner.next()方法,也就是说只要该客户端不下线,服务端就会一直在这个循环中,这将导致其他的想要建立连接的客户端无法完成。
当然,不使用while循环也是不可以的,因为如果没有while循环,客户端发送一次请求,服务端就会把连接给断掉,如果这个客户端还想继续建立连接,就需要重新建立连接,重新创建Socket对象,但是上述代码中的客户端只能创建一次socket,所以这时候就无法再建立连接了。
要想解决这个问题,就需要TCP服务端支持多线程进行操作。
TCP服务端(支持多个客户发送请求)
支持多个客户发送请求用到多线程的知识,可以提供多线程版本的服务端和线程池版本的服务端
多线程版本服务端
服务端的核心在于处理多个客户请求,也就是processConnection方法需要支持多线程,也就是说每个客户端的请求都会有一个线程进行处理。
多线程版本的服务端在客户端连接少的情况下是合适的,但是当客户端连接的量比较大的时候就不合适了,会有大量的线程的创建和销毁 ,资源耗费比较严重,所以可以考虑使用线程池来处理processConnection()方法。
线程池版本服务端
关于TCP的长连接和短连接
短连接工作过程
1、客户端与服务端建立连接
2、客户端向服务端发送请求
3、读取响应
4、关闭连接
特点:短连接一次通信一次连接,下一次通信需要重新建立连接,一次通信只会建立一次连接
长连接工作过程
1、客户端与服务端建立连接
2、客户端向服务端发送请求
3、读取响应
4、可根据需求再次发送请求(也是回到2)
5、重复2-4之间若干次,再决定是否关闭连接
特点:一次连接可多次发送请求,长连接的复用性更高