JavaEE传送门

JavaEE

JavaEE——网络通信基础

JavaEE——No.1 套接字编程(UDP)


目录

  • 套接字编程
    • TCP 的 socket
      • ServerSocket API
      • Socket API
    • 回显服务器
      • 改进版

套接字编程

TCP 的 socket

TCP socket 要掌握的类:

  1. ServerSokcet , 是创建 TCP 服务器 Socket.
  2. Socket, 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket。

ServerSocket API

构造方法:

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

方法:

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

Socket API

构造方法:

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

方法:

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

后两种方法, 通过 socket 可以获取到两个流对象, 分别用来读和写


回显服务器

服务器

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;/*** User: gu'jiu* Description:服务器*/
public class TcpEchoServer {//代码中会涉及到多个 socket 对象private ServerSocket listenSocket = null;public TcpEchoServer(int port) throws IOException {listenSocket = new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器启动! ");while(true) {//先调用 accept 来接受客户端的连接Socket clientSocket = listenSocket.accept();//再处理这个连接processConnection(clientSocket);    }}private void processConnection(Socket clientSocket) throws IOException {System.out.printf("[%s, %d] 客户端上线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {while(true) {//读取请求并解析Scanner scanner = new Scanner(inputStream);if(!scanner.hasNext()) {//读完了, 连接可以断开了System.out.printf("[%s, %d] 客户端下线!\n", clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}String request = scanner.next();//根据请求计算相应String response = process(request);//把响应写回给客户端PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(response);printWriter.flush();System.out.printf("[%s: %d] req: %s; reqs: %s\n", clientSocket.getInetAddress().toString(),clientSocket.getPort(), request, response);}} catch (IOException e) {e.printStackTrace();} finally {// 关闭 socketclientSocket.close();}}//回显public static String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(9092);server.start();}
}

部分代码解释

  1. 通过 clientSocket 来获取到 inputStream 和 outputStream.

  • 由于我们所说的 TCP 是面向字节流的, 和之前所介绍到的文件操作是完全相同的. 所以我们也是通过前面所学习过的这些字节流这些类, 来去完成我们数据的一个读写操作.

  • 这里我们的流对象就不再代表着磁盘文件了, 而是代表着 socket. 换句话说, 我们从这个 inputStream 读取数据, 就是从我们的网卡上读取数据; 从outputStream 写数据, 就是在向网卡中写数据.

  • cilentSocket 代表着的是服务器的网卡, 向 clientSocket 读写数据, 就是相当于向网卡中读写数据, 也就相当于在客户端中读写数据.

  1. socket 也是一个文件, 我们之前说过, 一个进程能同时打开的文件个数是有上限的.

    • listenSocket 是在 TCP 服务器中, 只有唯一一个对象, 就不太会把文件描述符表沾满(随着进程的退出, 自动释放)
    • 而 clientSocket 是在循环中, 每个客户端连上都要分配一个. 如果不关闭, 就会持续消耗文件描述符, 因此就需要把不再使用的 clientSocket 及时释放掉.

客户端

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;/*** User: gu'jiu* Description:客户端*/
public class TcpEchoClient {//客户端需要使用这个 socket 对象来建立链接Socket socket = null;public TcpEchoClient(String serverIP, int serverPort) throws IOException {//和服务器建立链接, 需要知道服务器的 ip 和端口号socket = new Socket(serverIP, serverPort);}public void start() {Scanner scanner = new Scanner(System.in);try(InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {while(true) {//从控制台读取数据, 构造成一个请求System.out.println("------------------");System.out.print("-> ");String request = scanner.next();//发送请求给服务器PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(request);printWriter.flush();//从服务器读取响应Scanner respScanner = new Scanner(inputStream);String response = respScanner.next();//把响应显示到界面上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", 9092);client.start();}
}

运行结果展示

启动服务器

此时没有客户端连接, accept( ) 是阻塞的状态

启动客户端

启动客户端之后的服务器 (accept( ) 阻塞结束)

在客户端输入"HelloGujiu"

关闭客户端后的服务器


改进版

一个服务器一般情况下, 都给多个客户端提供服务.

我们勾选一下 "Allow multiple instances"

JavaEE——No.1 套接字编程(内含打开方式)

这时我们启动两个客户端, 但是这时的服务器并没有出现两次 “客户端上线!”

为什么会出现这种情况呢?

  • 如果没有客户端建立连接, 服务器就会阻塞到 accept

  • 如果有一个客户端过来了, 此时就会进入 start() 方法

  • 此时代码就阻塞在 .hasNext() 这里

  • 于是, 我们就无法第二次调用到 accept, 也就无法处理第二个客户端了

为什么UDP 没有这个问题, TCP 有这个问题呢?

  • UDP 客户端直接发消息即可

  • TCP 建立连接之后, 要处理客户端的多次请求, 才导致无法快速的调到 accept (长连接)

    如果 TCP 每个连接只处理一个客户端的请求, 也能够保证快速调到 accept (短连接)

解决方案

使用线程池

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.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** User: gu'jiu* Description:服务器*/
public class TcpEchoServer {//代码中会涉及到多个 socket 对象private ServerSocket listenSocket = null;public TcpEchoServer(int port) throws IOException {listenSocket = new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器启动! ");ExecutorService service = Executors.newCachedThreadPool();while(true) {//先调用 accept 来接受客户端的连接Socket clientSocket = listenSocket.accept();//再处理这个连接service.submit(new Runnable() {@Overridepublic void run() {try {processConnection(clientSocket);} catch (IOException e) {e.printStackTrace();}}});}}private void processConnection(Socket clientSocket) throws IOException {System.out.printf("[%s, %d] 客户端上线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {while(true) {//读取请求并解析Scanner scanner = new Scanner(inputStream);if(!scanner.hasNext()) {//读完了, 连接可以断开了System.out.printf("[%s, %d] 客户端下线!\n", clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}String request = scanner.next();//根据请求计算相应String response = process(request);//把响应写回给客户端PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(response);printWriter.flush();System.out.printf("[%s: %d] req: %s; reqs: %s\n", clientSocket.getInetAddress().toString(),clientSocket.getPort(), request, response);}} catch (IOException e) {e.printStackTrace();} finally {// 关闭 socketclientSocket.close();}}public static String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(9092);server.start();}
}

JavaEE——No.2 套接字编程(TCP)相关推荐

  1. 套接字编程--TCP

    一.socket编程 socket本身有"插座"的意思,因此用来描述网络连接的一对一关系."在TCP/IP协议中,"TP地址+TCP或端口号"唯一标识 ...

  2. 套接字编程-TCP网络编程

    文章目录 套接字地址结构 通用套接字地址数据结构 以太网协议的套接字地址数据结构 Netlink协议套接字地址结构 TCP网络编程 套接字初始化socket() domain type protoco ...

  3. Linux——TCP协议与相关套接字编程

    一.TCP协议概念 与UDP协议相同,TCP协议也是应用在传输层的协议.虽然都是应用在传输层,但是使用方式和应用场景上大不一样.TCP协议具有:有连接(可靠).面向字节流的特点. (一).有连接 所谓 ...

  4. 计网实验原理-TCP/UDP套接字编程

    计算机网络自顶向下结构--第7版 第二章实验,套接字编程 代码运行环境:window10,python 3.8.对于书上代码略作修改. 进程与计算机网络之间的接口 多数应用程序是由通信进程队组成的,每 ...

  5. 「计算机网络:自顶向下方法阅读笔记」套接字编程:生成网络应用

    <计算机网络:自顶向下方法>阅读笔记系列 学习套接字编程就是探讨一下网络应用程序是如何实际编写的. 先回顾一下套接字:网络应用是由一对程序(客户程序和服务器程序)组成,位于两个不同的端系统 ...

  6. TCP和UDP套接字编程

    一.Socket简单介绍 如果要在应用层调用传输层的服务,进行相关程序的设计,就要涉及到套接字编程.套接字也称之为Socket,本质上它就是利用传输层提供的一系列Api来进行网络应用程序的设计. 网络 ...

  7. 套接字编程---2(TCP套接字编程的流程,TCP套接字编程中的接口函数,TCP套接字的实现,TCP套接字出现的问题,TCP套接字多进程版本,TCP套接字多线程版本)

    TCP模型创建流程图 TCP套接字编程中的接口 socket 函数 #include <sys/types.h> /* See NOTES */ #include <sys/sock ...

  8. 20181225 基于TCP/IP和基于UDP/IP的套接字编程

    一.TCP/IP的套接字编程 服务器端代码: import socket​server = socket.socket() # 默认是基于TCP# 基于TCP的对象serve=socket.socke ...

  9. tcp套接字编程模型

    1. tcp套接字编程 用下面的一张图可以清楚表示: 下面的python实现也很清晰: server: def tcplink(sock, addr):print 'Accept new connec ...

最新文章

  1. 直播预告丨深耕用户价值,实战保险业数字化升级
  2. LeetCode_2_两数相加
  3. 砸4亿美元,GE豪赌的全球最大风力发电机到底多大?
  4. android日期选择滚轮框架,GitHub - liwenzhi/wheelview: 滚轮效果的View,日期选择器
  5. C++编程积累——C++实现十进制与八进制之间的互相转换
  6. mac破解wifi密码
  7. 怎么读取瑞萨mcu的 checksum_为物联网设备选择一颗“安全”的MCU
  8. bzoj20892090: [Poi2010]Monotonicity
  9. linux系统开启端口命令
  10. java WinRM 远程连接 windows10 执行脚本
  11. Python学习第2天:入门必备(基础篇)
  12. Elasticsearch 地理位置查询
  13. Cotherm 多物理场耦合方案
  14. MIS系统(12)- 订单管理
  15. mysql磁盘阵列部署_部署磁盘阵列
  16. 随机生成一个有向无环图
  17. Desktop Computer操作系统之GUI发展
  18. Blockathon2018(上海)顺利结束,9个项目打开区块链落地新思路
  19. JavaScript(JS)有一组英文歌曲,按照歌曲名称的字母顺序从“A”到“Z”顺序排列,保存在一个数组中。
  20. IBM系列企业云计算产品和服务正式亮相

热门文章

  1. 计算机无法识别出cd驱动器,迅捷(FAST)免驱无线网卡插到电脑上无法识别CD驱动器怎么解决?【图解】...
  2. mysql权限层级体系_数据库mysql有哪些权限?层级有哪些?
  3. 一个简单音乐播放器的java实现(一)
  4. Scratch软件编程等级考试四级——20210320
  5. 小私企老板的痛病通病
  6. Error in invoking target 'agent nmhs' of makefile
  7. 第四章 大数定律与中心极限定理(总结)
  8. nacos安装配置和单机部署教程
  9. COSELF 次元秀场伦敦时装周预告 #虚拟时尚
  10. 中国itunes ip 记录