IT评测·应用市场-qidao123.com
标题:
5.6.3 网络套接字章 TCP服务器客户端
[打印本页]
作者:
万万哇
时间:
2024-10-27 06:41
标题:
5.6.3 网络套接字章 TCP服务器客户端
1.0 ServerSocket 服务端 API
概念
ServerSocket 是
创建TCP服务端
Socket的API。
TCP是字节省,我们并不需要像UDP那样有个专门的数据报,换句话来说一个tcp数据报,就是一个字节数组
ServerSocket 构造方法
方法署名方法阐明ServerSocket(int port)创建一个服务端流套接字Socket,并绑定到指定端口
ServerSocket 方法
方法签 名方法阐明Socket accept()开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket 对象,并基于该Socket建立与客户端的连接,否则壅闭等候Socket accept()关闭此套接字
2.0 Socket 客户端/服务端 API
概念
Socket 是客户端Socket,或服务端中吸收到客户端建立连接(accept方法)的请求后,返回的服务端 Socket。
不管是客户端还是服务端Socket,都是双方建立连接以后,生存的对端信息,及用来与对方收发数据 的。
Socket 既可以客户端用也可以服务器用
构造方法
方法署名方法阐明Socket(String host, int port)创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的 进程建立连接
Socket 方法
方法署名方法阐明InetAddress getInetAddress()返回套接字所连接的地点InputStream getInputStream()返回此套接字的输入流OutputStream getOutputStream()返回此套接字的输出流
3.0 短连接和长连接
TCP程序涉及到两种写法
1.短连接
一个链接中只能传输一次请求和相应
2.长连接
一个连接中,可以传输多次请求和相应
长连接与短连接在代码的区别就是有无while死循环,加了死循环,只要建立连接且不break,就可以一直通讯
4.0 TCP的回显服务器
概念
服务器启动后在while循环时,第一步不是吸收客户端请求,而是与客户端连接
不用握手,握手时系统内科负责的,写代码的过程是感受不到的
连接是在握手之后的处理
一次IO经历两个部分,1等(壅闭) 2拷贝数据
存在的题目与解决
Q1:ServerSocket中的队列是啥?
在系统内核眼里,ServerSocket这个对象也是一个文件,ServerSocket里有一个队列,当客户端和服务器实验建立连接的时间,服务器就会和客户端建立连接时,服务器就会和客户端进行一系列数据交互,称为"握手"(这一步由操作系统完成),这个过程建立完了之后,连接就建立好了,ServerSocket队列中就会添加这些建立好连接的对象**(生产),
要处理这些连接,就会取队列元素
(消费)**,每个ServerSocket对象都有一个队列
Q2:程序中建立连接的过程
一个服务器,要对应很多的客户端,服务器内核里有很多客户端的连接(已经握手了),但是应用程序还是要一个一个的建立连接,内核的连接就像一个一个"待服务项",这些代服务项在一个队列的数据布局中,应用程序就要一个一个的完成这些任务,要完成任务就需要serverSocket.accept方法取任务,如果客户端没 有连接,那么accept就会壅闭
Q3:为什么serverSocket.accept返回的是Socket?
, Socket就像耳麦(耳机加麦克风,耳机是InputStream,麦克风是OutputStream)一样,直接可以跟对方相互说话,通过Socket对象和对方进行网络通讯,实现由内核实现,我们可以这样理解:accept就是阐明我要指明某个客户端和这个服务器通讯,而Socket就是让这两个对象可以相互说话
Q4:客户端服务器通讯有几个流对象?
, 正确来说是两个Socket对象,服务器一个客户端一个,每个Socket对象(文件)对应两个流对象,有一个流入,一个流出
Q4;String next读取的题目?
next就是一直读数据,直到读到空白符竣事(空白符是一类字符,包括不限于: 换行’\n’,回车’\r’,空格,制表符,换页符,垂直制表符)
**Q5: 换行和回车的区别? ** 换行(‘\n’)是让光标到下一行,回车(‘\r’)是让光标回到行首,不会另起一行
Q6:flush的意义是什么?
IO操作是比较有开销的相比于访问内存,进行IO次数越多,程序越慢,为了提高服从,淘汰IO次数,使用一块内存充当缓冲区,写数据先写到缓冲区里,攒一波再进行IO,flush就可以刷新缓冲区,确保数据真的通过网卡发了出去,而不是残留在内存缓冲区,上述说的是全缓冲,而换行(‘\n’)刷新是行缓冲,行缓冲通常用于尺度输入输出(控制台上打印和输入我们通常输入enter linux本质就是’\n’,windows 是 ‘\r\n’),//但我们如今是往网卡/网络文件中写(全缓冲,不会受到’\n’影响)
Q7: 两个TCP类的关闭与否 ?
ServerSocket(只有一个,生命周期跟随程序,不关闭也没事),Socket每次来一个连接,就会有一个客户端和服务器建立连接,以是要用完就关闭
Q8:怎样解决TPC只支持单播特性
用多线程解决此题目,在主线程建立连接,在主线程建立连接完后,创建新线程专门用于处理客户端请求,可以使用Thread创建(只要服务器系统资源足够,就几个客户端连接都可以)
Q9: 多个服务器重复连接再断启发致服务器频繁创建释放线程
, 用线程池解决
Q10: 啥是C10M
10k 同一时候,有10k个客户端(1w个客户端) , 硬件好大概线程池就能解决, C10M 就是同一时候有1kw的客户端并发请求,这时间以达到高并发了
(其实总的来说解决高并发的方法就是
开源节省
,开源:引入更多的硬件资源,节省:
提高单位硬件资源能够处理的请求数
,本质上是淘汰线程数目,淘汰资源使用)
其中的一个重要解决高并发的方法 就是IO多路复用/IO多路转接,IO多路复用就是一个线程完成多组IO操作(比如对很多小吃摊位付钱,等候他们谁先好我就直接拿摊位的食物),多路复用是OS提供的另一组网络编程的API,这组API可以共同tcp udp 的api共同使用(去搜索 java NIO 写的服务器和当前tcp的差别挺大的)
代码示例
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;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TcpEchoServer {
private ServerSocket serverSocket = null;
// 此处不应该创建固定线程数目的线程池.
private ExecutorService service = Executors.newCachedThreadPool();//线程池
//构造方法
// 这个操作就会绑定端口号
public TcpEchoServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
// 启动服务器
public void start() throws IOException {
System.out.println("服务器启动!");//打印日志
//服务器启动后在while循环时,第一步不是接收客户端请求,而是与客户端连接
while (true) {
//把内核中已连接的对象获取到应用程序中了,这个过程类似于生产者消费者模型
//返回值是一个Socket
Socket clientSocket = serverSocket.accept();
//***************************************************************************************************************
//问题2:
// 单个线程,不能处理多个服务器请求,只能一个结束才能处理下一个
// 多线程解决此问题,在主线程建立连接,在主线程建立连接完后,创建新线程专门用于处理客户端请求
// Thread t = new Thread(() -> {
// processConnection(clientSocket);
// });
// t.start();
//问题1:
// 这个写法,是能自动关闭close,只要实现 Closeable 接口就可以这么写.
//这么写会有其他问题.processConnection和主线程是不同线程,执行processConnection的过程中,主线程的try就执行完毕了,这就导致clientSocket没用完就被关闭了
// try(Socket clientSocket = serverSocket.accept();){
// Thread t = new Thread(() -> {
// processConnection(clientSocket);
// });
// t.start();
//
// };
// 使用线程池, 来解决上述问题
service.submit(new Runnable() {
@Override
public void run() {
processConnection(clientSocket);
}
});
//***************************************************************************************************************
}
}
// 通过这个方法来处理一个连接的逻辑.
private void processConnection(Socket clientSocket) {
System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
// 接下来就可以1.读取请求, 2.根据请求计算响应, 3.返回响应三步走了.
// Socket 对象内部包含了两个字节流对象, 可以把这俩字节流对象获取到, 完成后续的读写工作
try (InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream()) {
// 一次连接中, 可能会涉及到多次请求/响应
while (true) {
// 1. 读取请求并解析. 为了读取方便, 直接使用 Scanner.(把字节流在内地转化为字符流)
Scanner scanner = new Scanner(inputStream);
//当没读到客户端数据的时候hasNext会一直阻塞,或者客户端退出,hashNext感知到客户端退出就break了
if (!scanner.hasNext()) { //如果读到eof,就读完了,就退出循环
// 读取完毕, 客户端下线.
System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
break;
}
// 这个代码暗含一个约定, 客户端发过来的请求得是文本数据, 同时还得带有空白符作为分割. (比如换行这种)
//next就是一直读数据,直到读到空白符结束(空白符是一类字符,包括不限于: 换行'\n',回车'\r',空格,制表符,换页符,垂直制表符)
//换行('\n')是让光标到下一行,回车('\r')是让光标回到行首,不会另起一行
String request = scanner.next();
// 2. 根据请求计算响应
String response = process(request);
// 3. 把响应写回给客户端. 把 OutputStream 使用 PrinterWriter 包裹一下, 方便进行发数据.
PrintWriter writer = new PrintWriter(outputStream);
//使用 PrintWriter 的 println 方法, 把响应返回给客户端.
//此处用 println, 而不是 print 就是为了在结尾加上 \n . 方便客户端读取响应, 使用 scanner.next 读取.
writer.println(response);
//这里还需要加一个 "刷新缓冲区" 操作.
//IO操作是比较有开销的相比于访问内存,进行IO次数越多,程序越慢
//为了提高效率,减少IO次数,使用一块内存充当缓冲区,写数据先写到缓冲区里,攒一波再进行IO,flush就可以刷新缓冲区,确保数据真的通过网卡发了出去,而不是残留在内存缓冲区
//上述说的是全缓冲,而换行('\n')刷新是行缓冲,行缓冲通常用于标准输入输出(控制台上打印和输入我们通常输入enter linux本质就是'\n',windows 是 '\r\n'),//但我们现在是往网卡/网络文件中写(全缓冲,不会受到'\n'影响)
writer.flush();
// 日志, 打印当前的请求详情.
System.out.printf("[%s:%d] req: %s, resp: %s\n", clientSocket.getInetAddress().toString(), clientSocket.getPort(),
request, response);
}
} catch (IOException e) {
e.printStackTrace();//捕获try的IO异常
} finally {
// 在 finally 中加上 close 操作, 确保当前 socket 被及时关闭!!
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TcpEchoServer server = new TcpEchoServer(9090);
server.start();
}
}
复制代码
5.0 TCP 客户端
题目与解决
Q1: 代码中Scanner 和 PintWriter 为什么可以不关闭
, 流对象中持有的资源,为两个部分 1.内存(对象销毁,内存也就回收了,while循环一圈,当break天然销毁了), 2 scanner 和 printwriter 没有持有文件描述符 他们持有的是inputstram和outputstream的引用,也就是持有其他流对象的引用,而inputstram和outputstream 写在try括号内,会主动关闭的,更正确来说是socket对象持有inputstram和outputstream 只要关闭socket就可以了
代码示例
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 {
private Socket socket = null;
// 要和服务器通信, 就需要先知道, 服务器所在的位置.,这里的ip和端口号都是服务器的
public TcpEchoClient(String serverIp, int serverPort) throws IOException {
// 这个 new 操作完成之后, 就完成了 tcp 连接的建立.
//这一段代码本质做了相当多事,在网络原理中会讲述到
socket = new Socket(serverIp, serverPort);
}
public void start() {
System.out.println("客户端启动");
Scanner scannerConsole = new Scanner(System.in);
try (InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream()) {
while (true) {
// 1. 从控制台输入字符串.
System.out.print("-> ");
String request = scannerConsole.next();
// 2. 把请求发送给服务器
PrintWriter printWriter = new PrintWriter(outputStream);
// 使用 println 带上换行. 后续服务器读取请求, 就可以使用 scanner.next 来获取了
printWriter.println(request);
// 不要忘记 flush, 确保数据是真的发送出去了!!
printWriter.flush();
// 3. 从服务器读取响应.
Scanner scannerNetwork = new Scanner(inputStream);
String response = scannerNetwork.next();
// 4. 把响应打印出来
System.out.println(response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);
client.start();
}
}
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/)
Powered by Discuz! X3.4