当前位置: 首页 > news >正文

申请网站空间有哪几种方式网站站点

申请网站空间有哪几种方式,网站站点,我想做自己网站怎么做,网站建设 有限公司网络编程:写一个应用程序,让这个程序可以使用网络通信。这里就需要调用传输层提供的 api。 Socket套接字 传输层提供协议,主要是两个: UDP和TCP 提供了两套不同的 api,这api也叫做socket api。 UDP和 TCP 特点对比: UDP: 无连…

网络编程:写一个应用程序,让这个程序可以使用网络通信。这里就需要调用传输层提供的 api。

Socket套接字

传输层提供协议,主要是两个: UDP和TCP
提供了两套不同的 api,这api也叫做socket api。

UDP和 TCP 特点对比:

  • UDP: 无连接,不可靠传输,面向数据报,全双工
  • TCP : 有连接,可靠传输,面向字节流,全双工

有连接,无连接:客户端和服务器之间,彼此之间使用内存空间保存对端的信息,双方都保存这个信息此时“连接”就出现了。

可靠传输,不可靠传输:不是说,A 给 B 发的消息 100% 能到(这个要求太难了)。A 尽可能的把消息传给 B.并且在传输失败的时候,A 能感知到,**或者在传输成功的时候,也能知道自己传输成了。**可靠传输效率低,不可靠传输效率高。

面向字节流,面向数据报:

  1. TCP 和 文件操作类似,都是“流”式的(由于这里传输的单位是字节,称为字节流)
  2. UDP 是面向数据报,读写的基本单位是一个 UDP 数据报(包含了一系列的数据/属性)

全双工,半双工:

  1. 全双工:一个通道,可以双向通信
  2. 半双工:一个通道,只能单向通信

UDP数据报套接字编程

DatagramSocket API

DatagramSocket API 是一个 Socket 对象。操作系统,使用文件这样的概念,来管理一些软硬件资源,操作系统也是使用文件的方式来管理网卡的。表示网卡的这类文件,称为 Socket 文件。Java 中的 socket 对象,就对应这系统里的 socket 文件。

构造方法:

方法签名方法说明
DatagramSocket()创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口 (一般用于客户端)
DatagramSocket(int port)创建一个UDP数据报套接字的Socket,绑定到本机指定的端口**(一般用 于服务端)**

客户端使用哪个端口,系统自动分配。但是服务器使用哪个端口,是手动指定的。对于服务器来说,需要有一个固定的端口号,方便其他客户端找到。一个客户端的主机,上面运行的程序很多,天知道你手动指定的端口是不是被别的程序占用了。让系统自动分配一个端口是更明智的选择。服务器是完全在程序猿手里控制的。程序猿可以把服务器上的多个程序安排好,让他们使用不同的端口。

普通方法:

方法签名方法说明
void receive(DatagramPacket p)从此套接字接收数据报(如果没有接收到数据报,该方法会阻 塞等待)
void send(DatagramPacket p)从此套接字发送数据报包(不会阻塞等待,直接发送)
void close()关闭此数据报套接字

DatagramPacket API

表示了一个 UDP 数据报,代表了系统中设定的 UDP 数据报的二进制结构。

构造方法:

方法签名方法说明
DatagramPacket(byte[] buf, int length)构造一个DatagramPacket以用来接收数据报,接收的数据保存在 字节数组(第一个参数buf)中,接收指定长度(第二个参数 length)
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)构造一个DatagramPacket以用来发送数据报,发送的数据为字节 数组(第一个参数buf)中,从0到指定长度(第二个参数 length)。address指定目的主机的IP和端口号

普通方法:

方法签名方法说明
SocketAddress getSocketAddress()SocketAddress对象包含了IP地址和端口号
InetAddress getAddress()从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取 接收端主机IP地址
int getPort()从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号
byte[] getData()获取数据报中的数据。udp 数据报载荷部分(完整的应用层数据报)

示例一:回显服务器(echo server).

回显服务器(echo server):客户端发啥,服务器返回啥.

// UDP 的 回显服务器.
// 客户端发的请求是啥, 服务器返回的响应就是啥.
public class UdpEchoServer {private DatagramSocket socket = null;// 参数是服务器要绑定的端口public UdpEchoServer(int port) throws SocketException {socket = new DatagramSocket(port);}// 使用这个方法启动服务器.public void start() throws IOException {System.out.println("服务器启动!");while (true) {// 反复的, 长期的执行针对客户端请求处理的逻辑.// 一个服务器, 运行过程中, 要做的事情, 主要是三个核心环节.// 1. 读取请求, 并解析DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);socket.receive(requestPacket);//    这样的转字符串的前提是, 后续客户端发的数据就是一个文本的字符串.String request = new String(requestPacket.getData(), 0, requestPacket.getLength());// 2. 根据请求, 计算出响应String response = process(request);// 3. 把响应写回给客户端//    此时需要告知网卡, 要发的内容是啥, 要发给谁.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 {UdpEchoServer server = new UdpEchoServer(9090);server.start();}
}

客户端代码:

public class UdpEchoClient {private DatagramSocket socket = null;private String serverIp;private int serverPort;// 服务器的 ip 和 服务器的端口.public UdpEchoClient(String ip, int port) throws SocketException {serverIp = ip;serverPort = port;// 这个 new 操作, 就不再指定端口了. 让系统自动分配一个空闲端口.socket = new DatagramSocket();}// 让这个客户端反复的从控制台读取用户输入的内容. 把这个内容构造成 UDP 请求, 发给服务器. 再读取服务器返回的 UDP 响应// 最终再显示在客户端的屏幕上.public void start() throws IOException {Scanner scanner = new Scanner(System.in);System.out.println("客户端启动!");while (true) {// 1. 从控制台读取用户输入的内容System.out.print("-> "); // 命令提示符, 提示用户要输入字符串.String request = scanner.next();// 2. 构造请求对象, 并发给服务器.DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,InetAddress.getByName(serverIp), serverPort);socket.send(requestPacket);// 3. 读取服务器的响应, 并解析出响应内容.DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);String response = new String(responsePacket.getData(), 0, responsePacket.getLength());// 4. 显示到屏幕上.System.out.println(response);}}public static void main(String[] args) throws IOException {UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);client.start();}
}

可以启动多个客户端,服务器也是可以应对的。IDEA 上启动多个程序,需要稍微设置一下:

image-20230828095652845

其他人的电脑不能访问我这个程序。原因是:网络环境的现状,NAT 机制是主流。NAT 机制下,就把 IP 地址分成了外网 IP 和内网 IP.(内网IP 不能直接访问)。

示例二:翻译服务器

翻译服务器:请求是一些英文单词,响应则是对应的中文翻译。代码很多和回显服务器相同,所以继承UdpEchoServer 来实现代码的复用。

public class UdpDictServer extends UdpEchoServer {private Map<String, String> dict = new HashMap<>();public UdpDictServer(int port) throws SocketException {super(port);dict.put("cat", "小猫");dict.put("dog", "小狗");dict.put("hello", "你好");// 可以在这里继续添加千千万万个单词. 使每个单词都有一个对应的翻译.}// 是要复用之前的代码, 但是又要做出调整.@Overridepublic String process(String request) {// 把请求对应单词的翻译, 给返回回去.return dict.getOrDefault(request, "该词没有查询到!");}public static void main(String[] args) throws IOException {UdpDictServer server = new UdpDictServer(9090);// start 不需要重新再写一遍了. 直接就复用了之前的 start !server.start();}
}

TCP流套接字编程

TCP 分量要比 UDP 更重用的,用的更多的协议。

TCP 提供的 api 也是主要有两个类

  • ServerSoeket:给服务器使用的 socket
  • Socket: 既会给服务器使用,也会给客户端使用

字节流,一个字节一个字节进行传输的。一个 tcp 数据报,就是一个 字节数组 byte[]。

ServerSocket API

ServerSocket 构造方法:

方法签名方法说明
ServerSocket(int port)创建一个服务端流套接字Socket,并绑定到指定端口

ServerSocket 方法:

方法签 名方法说明
Socket accept()开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket 对象,并基于该Socket建立与客户端的连接,否则阻塞等待
void close()关闭此套接字

Socket API

Socket 构造方法:

方法签名方法说明
Socket(String host, int port)创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的 进程建立连接

Socket 方法:

方法签名方法说明
InetAddress getInetAddress()返回套接字所连接的地址
InputStream getInputStream()返回此套接字的输入流
OutputStream getOutputStream()返回此套接字的输出流

ServerSocket的accept()方法负责产生连接,产生连接的类型是Socket。Socket负责后续的连接后的通信。 ServerSocket的accept()是在服务器中使用,当服务器执行到 accept 的时候, 此时客户端可能还没来呢。accept 就会阻塞到有客户端连接成功为止。

accept()把内核中的连接获取到应用程序中了,这个过程类似于,"生产者消费者模型”。

image-20230829100509766

TCP版本的回显服务器

客户端代码,要做的事情:

  1. 读取请求并解析.
  2. 根据请求计算响应
  3. 把响应写回给客户端.
public class TcpEchoServer {private ServerSocket serverSocket = null;// 这个操作就会绑定端口号public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);}// 启动服务器public void start() throws IOException {System.out.println("服务器启动");while (true){Socket clientSocket = serverSocket.accept();processConnection(clientSocket);}}// 通过这个方法来处理一个连接的逻辑.private void processConnection(Socket clientSocket) throws IOException {System.out.printf("[%s:%d]客户端上线\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());// 接下来就可以读取请求, 根据请求计算响应, 返回响应三步走了.// Socket 对象内部包含了两个字节流对象, 可以把这俩字节流对象获取到, 完成后续的读写工作try (InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()){// 一次连接中, 可能会涉及到多次请求/响应while (true){//1. 读取请求并解析. 为了读取方便, 直接使用 Scanner.Scanner sc = new Scanner(inputStream);if(!sc.hasNext()){//读取完毕,客户端下线System.out.printf("[%s:%d]客户端下线\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}// 这个代码暗含一个约定, 客户端发过来的请求, 得是文本数据, 同时, 还得带有空白符作为分割. (比如换行这种)String request = sc.next();// 2. 根据请求计算响应String response = process(request);// 3. 把响应写回给客户端. 把 OutputStream 使用 PrinterWriter 包裹一下, 方便进行发数据.PrintWriter writer = new PrintWriter(outputStream);//    使用 PrintWriter 的 println 方法, 把响应返回给客户端.//    此处用 println, 而不是 print 就是为了在结尾加上 \n . 方便客户端读取响应, 使用 scanner.next 读取.writer.println(response);//    这里还需要加一个 "刷新缓冲区" 操作.writer.flush();// 日志, 打印当前的请求详情.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 {TcpEchoServer server = new TcpEchoServer(9090);server.start();}
}

客户端代码,要做的事情:
1.从控制台读取用户的输入
2.把输入的内容构造成请求并发送给服务器
3.从服务器读取响应
4.把响应显示到控制台上

public class TcpEchoClient {private Socket socket = null;// 要和服务器通信, 就需要先知道, 服务器所在的位置.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();}
}

多线程版本的会先服务器

当前代码还存在一个很大的问题。多个客户端访问会出现bug。当第一个客户端连接好了之后,第二个客户端,不能正确被处理服务器。看不到客户端上线,同时客户端发来的请求也无法被处理。当第一个客户端退出之后,之前第二个客户端发的请求,就能正确响应了。

上面的单线程代码中,serverSocket.accept(),建立一个连接,但是在进入processConnection()后,连接不断开,下一个连接就没办法连接上。主线程一个线程,又要建立连接,又要进行通信,只有通信结束连接断开之后,才会有下一个连接。要想解决这个问题我们就需要用多线程来解决,主线程负责建立连接,新建立的线程负责连接后的通信,各司其职就能解决这个问题。

public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);}// 启动服务器public void start() throws IOException {System.out.println("服务器启动");while (true){Socket clientSocket = serverSocket.accept();Thread t = new Thread(()->{processConnection(clientSocket);});}}

经过上述改进,只要服务器系统资源足够,有几个客户端都是可以的了。

TCP 程序的时候,涉及到两种写法

  1. 一个连接中只传输一次请求和响应.短连接

  2. 一个连接中,可以传输多次请求和响应.长连接

上面代码属于长连接。其实如果我们刚才的代码,不写成这个样子。比如要求每个客户端只能发一次请求,发完就断开。上述情况能得到一定的缓解,但是还是有类似的问题的。处理多个消息,自然就会延长 processConnection 的执行时间,后续的连接也得等到上个连接断开后才能继续连接。

现在是有一个连接,就有一个新的线程。如果很多客户端,频繁的来连接/断开,服务器就涉及到频繁创建/释放线程了。我们使用线程池会比较好的解决这个问题。

    // 此处不应该创建固定线程数目的线程池.private ExecutorService service = Executors.newCachedThreadPool();// 启动服务器public void start() throws IOException {System.out.println("服务器启动");while (true){Socket clientSocket = serverSocket.accept();// 使用线程池, 来解决上述问题service.submit(new Runnable() {@Overridepublic void run() {processConnection(clientSocket);}});}}

虽然使用线程池,避免了频繁创建销毁线程。但是毕竟是每个客户端对应一个线程。如果服务器对应的客户端很多,服务器就需要创建出大量的线程.对于服务器的开销是很大的。当客户端进一步增加,线程数目进一步增加系统的负担就越来越重,响应速度也会大打折扣。

< C10K问题:同一时刻,有 10k 个客户端(1w 个客户端),通过前面的一些技术手段,硬件设备,是可以处理好.(线程池之类的)
随着互联网的发展,客户端越来越多,请求也越来越多,进而引发了C10M问题。
C10M: 同一时刻,有 1kw 的客户端并发请求。引入了很多技术手段.其中一个非常 有效/必要 的手段,IO 多路复用/IO 多路转接.
IO 多路复用:一个线程完成多个IO操作。 不是说使用了这一个方法就能解决C10M问题,但是解决 高并发(C10M) 重要手之一。

解决高并发,就是四个字:

  1. 开源: 引入更多的硬件资源 (多核CPU)
  2. 节流: 提高单位硬件资源能够处理的请求数。同样的请求数,消耗的硬件资源更少。

IO 多路复用,就是节流的方式。同样的请求,消耗的硬件资源更少了。本质上就是减少线程的数量。操作系统提供的另一组网络编程的 API 可以和 tcp udp api 配合使用的。可以去搜索 Java NIO 一些关键词来学习IO多路复用。

http://www.yidumall.com/news/29886.html

相关文章:

  • 做网站时如何给文字做超链接网站seo专员
  • 大连网站快速排名提升怎么优化网站
  • 专门做高端网站设计的云华设计官方app下载安装
  • 备案查询系统绍兴seo网站管理
  • 外贸网站源码去一品资源提高工作效率的方法
  • 使用wget对网站做镜像百度推广一般要多少钱
  • 充值选建设银行打不开网站优化水平
  • iis如何做同时运行两个网站80端口投百度做广告效果怎么样
  • seochinaz查询蜗牛精灵seo
  • 旧版wordpress长岭网站优化公司
  • 如何做网站拓扑结构图云南疫情最新情况
  • h5小游戏源码大全google搜索引擎优化
  • excel 表格 做的网站台州seo服务
  • 网站功能优化蚂蚁链接bt链接
  • 襄阳做网站的cms自助建站系统
  • 个人网站怎么做淘宝客专门做排行榜的软件
  • wordpress企业站主题百度seo优化收费标准
  • 做网站怎么切片百度首页百度
  • c 可以用来做网站吗推广软件下载
  • wordpress收录查询seo网站推广平台
  • dw简单网页制作代码seo标题生成器
  • 网站怎么做数据备份互联网广告投放平台加盟
  • 番禺网站建设开发爱网站关键词查询工具长尾
  • 找人做网站设计 哪个平台可以找百度搜索引擎优化方式
  • 网站外链建设教程百度认证服务平台
  • 迪哥哪个网站上做游戏直播互联网营销师培训教程
  • 百度头条怎么做网站seo推广有哪些公司
  • 电脑系统做的好的网站好青岛网站建设与设计制作
  • 长安镇仿做网站外国搜索引擎登录入口
  • 二维码插件wordpress北京自动seo