【计算机网络】—网络编程(socket)02
创始人
2024-01-29 11:29:39

目录

一、网络编程的概念

二、UDP数据报套接字编程

2.1 回显服务器代码

 2.2 翻译程序(英译汉)

三、TCP数据报套接字编程

3.1回显服务器

3.2 翻译服务器


一、网络编程的概念

网络编程:指网络上的主机,通过不同的进程,以编程的方式实现网络通信(网络数据传输)

发送端和接收端只是相对的,只是一次网络数据传输产生数据流向后的概念

Socket套接字:由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程。操作系统给应用程序提供的一组API。

TCP的特点:

  • 有连接:需要连接成功才能发送数据(类似打电话)
  • 可靠传输:发送方知道接收方有没有收到数据(显示已读未读)
  • 面向字节流:以字节为单位传输(类似文件操作中的字节流)
  • 有接收缓冲区,也有发送缓冲区:
  • 全双工:一条通道,双向通信
  • 大小不限

UDP的特点:

  • 无连接 :不需要接通,直接发数据(类似发微信)
  • 不可靠传输:发送方不知道数据是否已被对方接收到
  • 面向数据报:以数据报为单位传输()
  • 有接收缓冲区,无发送缓冲区:
  • 大小受限:一次最多传输64k
  • 全双工:一条通道,双向通信

二、UDP数据报套接字编程

UDP Socket 主要涉及两个类:DatagramSocket、DatagramPacket

DatagramSocket对象,对应到操作系统的一个socket文件(文件除了普通文件,还包括硬件设备 / 软件资源)

socket文件对应“网卡”这种硬件设备,从socket文件读写数据,就是读写网卡中的信息


2.1 回显服务器代码

构造一个回显服务器(请求和响应相同):包括客户端和服务器的代码

(1)服务器端代码:

构造函数中关于port的指定:服务器需要手动指定端口号port(客户端需要根据端口号访问服务器),而客户端会自动指定端口(不知道客户端已经被占用了哪些端口),由于客户端为主动发起请求的一方,因此需要知道服务器的地址+端口

1. 启动服务器: start函数

2.读取客户端发来的请求,并将其解析为一个字符串

3.根据请求计算响应(是最复杂的过程):String response = process(request)

4.把响应写回到客户端(需要先把数据构造成一个数据报) 

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;/*回显服务端的代码:
1.先指定端口号接收,便于指定进程处理该信息
2.启动服务器:读取请求(若读到,则需要返回)----->根据请求计算响应*/public class UdpEchoServer {private DatagramSocket socket = null;  //创建实例socket,进行网络编程的前提public UdpEchoServer(int port) throws SocketException {    //构造方法传入端口进行绑定socket = new DatagramSocket(port);}
// 1. 启动服务器public void start() throws IOException {System.out.println("启动服务器!");while (true) {
//DatagramPacket把一个字节数组(byte[])进行包装
// 2.读取客户端发来的请求,并将其解析为一个字符串DatagramPacket requestPacket = new DatagramPacket(new byte[1024], 1024);socket.receive(requestPacket);   //接收数据(一个UDP数据报)String request = new String(requestPacket.getData(),0,requestPacket.getLength(),"UTF-8");
//3. 根据请求计算相应(是最复杂的过程)String response = process(request);
//4.把响应写回到客户端(需要先把响应 数据构造成一个数据报),同时指定IP和端口
// response.getBytes()获取响应字节数组,response.getBytes().length获取字节数组长度;requestPacket.getSocketAddress():指定发回去的地址(也即是客户端)DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);//打印日志 %s:打印字符串    %d:打印有符号位十进制整数String log = String.format("[%s:%d] req: %s; resp: %s",requestPacket.getSocketAddress().toString(),requestPacket.getPort(),request,response);System.out.println(log);}}//由于是回响服务器,响应和请求是一样的public String process(String request) {return request;    //返回请求值}public static void main(String[] args) throws IOException {UdpEchoServer server = new UdpEchoServer(9090);server.start();//启动服务器}
}

(2)客户端代码:

1.从控制台读取用户输入的信息

2. 把读取的内容构造成一个UDP请求,并发送

3.从服务器读取响应数据,并解析

4.把响应结果显示到控制台

import javax.sound.sampled.Port;
import java.io.IOException;
import java.net.*;
import java.util.Scanner;/*服务器需要手动指定端口号,而客户端会自动指定端口,无需手动执行
* */
public class UdpEchoClient {private DatagramSocket socket = null;private String serverIP;private int serverPort;public UdpEchoClient(String ip,int port) throws SocketException {   //不需要指定端口portsocket = new DatagramSocket();serverIP = ip;serverPort = port;}public void start() throws IOException {Scanner in = new Scanner((System.in));while (true) {// 1.从控制台读取用户输入的信息System.out.println("->");String request = in.next();// 2. 把读取的内容构造成一个UDP请求(String数据内容+目的地服务器地址),并发送DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,InetAddress.getByName(serverIP),serverPort);  //等价于InetSocketAddress,获取地址和IPsocket.send(requestPacket);// 3.从服务器读取响应数据,并解析DatagramPacket responsePacket = new DatagramPacket(new byte[1024],1024);socket.receive(responsePacket);String response = new String(responsePacket.getData(),0,responsePacket.getLength(),"utf-8");// 4.把响应结果显示到控制台System.out.printf("req: %s, resp: %s\n", request, response);}}public static void main(String[] args) throws IOException {UdpEchoClient client = new UdpEchoClient("127.0.0.1",9090);   //因为此处服务器在本机,所以指定的IP为本机环回IPclient.start();}
}

 开启多个客户端发送数据,按照下图进行配置

服务器输出结果:

 

 2.2 翻译程序(英译汉)

 (1)客户端程序不变,如上

(2)只需调整服务器代码,只要是修改process方法(根据请求处理响应)

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;public class UdpDictServer extends UdpEchoServer{private HashMap dict = new HashMap();public UdpDictServer(int port) throws SocketException {  //插入构造方法super(port);dict.put("cat","小猫");dict.put("dog","小狗");}// 重写process@Override    //注意重写的UdpEchoServer中的process方法不能是私有的public String process(String request) {return dict.getOrDefault(request,"该词无法被查询到");}public static void main(String[] args) throws IOException {UdpDictServer server = new UdpDictServer(9090);server.start();}
}

三、TCP数据报套接字编程

  • ServerSocket专门给TCP服务器使用
  • Socket给服务器和客户端使用

3.1回显服务器

1.读取请求

2.根据请求计算响应

3.把响应返回客户端

(1)服务器代码

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 TcpEpochServer {private ServerSocket serverSocket = null;   定义一个变量表示服务器    listen 原意为监听public TcpEpochServer(int port) throws IOException {   //服务器需要指定端口号serverSocket =  new ServerSocket(port);    //给服务器指定端口号}public void start() throws IOException {System.out.println("启动服务器!!");while (true) {//1.由于tcp是有连接的,需要先进行连接才能读数据  若无客户端与其建立连接,accept则会阻塞Socket clientSocket = serverSocket.accept();   //clientSocket完成后续操作processConnection(clientSocket);}}private void processConnection(Socket clientSocket) {System.out.printf("[%s:%d] 客户端建立连接", clientSocket.getInetAddress().toString(),clientSocket.getPort());try (InputStream inputStream = clientSocket.getInputStream()){try (OutputStream outputStream = clientSocket.getOutputStream()) {Scanner in = new Scanner(System.in);while (true) {  //循环处理每个请求//1.读取请求if (!in.hasNext()) {System.out.printf("[%s:%d] 客户端建立连接", clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}String request = in.next();//2.根据请求计算响应String response = process(request);//3.把响应返回客户端PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(response);printWriter.flush();  //刷新缓冲区System.out.printf("[%s:%d] req: %s,resp:%s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,response);}}}catch (IOException e) {e.printStackTrace();}finally {try {clientSocket.close();}catch (IOException e) {e.printStackTrace();}}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEpochServer server = =new TcpEpochServer(9090);server.start();}
}

(2)客户端代码(单个连接)

1.从控制台读取字符串数据

2.根据读取的数据构造请求,并把请求发送给服务器

3.从服务器读取响应,并解析

4.把结果显示到控制台上

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 TcpEpochClient {private Socket socket = null;   //客户端不用指定IP,由系统自动分配即可public TcpEpochClient(String serverIP,int serverPort) throws IOException {socket = new Socket(serverIP,serverPort);  //此处的端口号和IP表示的是服务器所有的,客户端通过超找到IP和端口进行连接}public void start() throws IOException {System.out.println("和服务器连接成功");Scanner in = new Scanner(System.in);try (InputStream inputStream = socket.getInputStream()) {try (OutputStream outputStream = socket.getOutputStream()) {while (true) {
//  1.从控制台读取字符串数据System.out.print("- >");String request = in.next(); //请求为控制台输入内容
//  2.根据读取的数据构造请求,并把请求发送给服务器PrintWriter printWriter = new PrintWriter(outputStream);//用·PrintWriter包装outputStreamprintWriter.println(request);  //打印请求printWriter.flush(); //写入之后就需要刷新,否则服务器不能及时显示
//  3.从服务器读取响应,并解析Scanner respScanner = new Scanner(inputStream);  //读取输入请求的数据流String response = respScanner.next();
//  4.把结果显示到控制台上System.out.printf("req: %s,resp: %s\n",request,response);}}}catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) throws IOException {TcpEpochClient client = new TcpEpochClient("127.0.0.1",9090);client.start();}
}

(2)客户端代码(建立多个连接)

由于服务器部分while处包含嵌套循环,导致内部循环未结束便不能进行外部循环,导致服务器不能与多个客户端建立连接 。

解决:主线程循环调用accept,有客户端连接时,则主线程创建一个新线程负责对客户端的若干请求(while循环处理请求)。由于多线程是并发执行的(宏观上是同时执行),由我们观察处理时就感觉到是各个线程独立执行,互不干扰,便就实现了多客户端连接。对如下代码进行改进即可。

改进1:每次accept成功,就创建一个线程,由其负责执行客户端的请求

    public void start() throws IOException {System.out.println("服务器启动!!");while (true) {   //此处包含两个循环(processConnection方法内部有一个循环)//1.由于tcp是有连接的,需要先进行连接才能读数据  若无客户端与其建立连接,accept则会阻塞Socket clientSocket = serverSocket.accept();   //clientSocket完成后续与客户端之间的沟通Thread t = new Thread(() ->{    //创建线程,使processConnection在线程内部执行processConnection(clientSocket);});t.start();}}

改进2:每次accept成功,就创建线程池,由其负责执行客户端的请求

    public void start() throws IOException {System.out.println("服务器启动!!");ExecutorService pool = Executors.newCachedThreadPool();  //创建线程池while (true) {   //此处包含两个循环(processConnection方法内部有一个循环)//1.由于tcp是有连接的,需要先进行连接才能读数据  若无客户端与其建立连接,accept则会阻塞Socket clientSocket = serverSocket.accept();   //clientSocket完成后续与客户端之间的沟通pool.submit(new Runnable() {@Overridepublic void run() {processConnection(clientSocket);}});}}

 完整代码:

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 TcpThreadEpochServer {private ServerSocket serverSocket = null;   //定义一个变量表示服务器public TcpThreadEpochServer(int port) throws IOException {   //服务器需要指定端口号serverSocket =  new ServerSocket(port);  //给服务器指定端口号}public void start() throws IOException {System.out.println("服务器启动!!");while (true) {   //此处包含两个循环(processConnection方法内部有一个循环)//1.由于tcp是有连接的,需要先进行连接才能读数据  若无客户端与其建立连接,accept则会阻塞Socket clientSocket = serverSocket.accept();   //clientSocket完成后续与客户端之间的沟通Thread t = new Thread(() ->{    //创建线程,使processConnection在线程内部执行processConnection(clientSocket);});t.start();}}private void processConnection(Socket clientSocket) {System.out.printf("[%s:%d] 客户端建立连接\n", clientSocket.getInetAddress().toString(),clientSocket.getPort());try (InputStream inputStream = clientSocket.getInputStream()){try (OutputStream outputStream = clientSocket.getOutputStream()) {Scanner in = new Scanner(System.in);while (true) {  //循环处理每个请求if (!in.hasNext()) {               //1.读取请求System.out.printf("[%s:%d] 客户端断开连接\n", clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}String request = in.next();String response = process(request);  //2.根据请求计算响应PrintWriter printWriter = new PrintWriter(outputStream);  //3.把响应返回客户端printWriter.println(response);printWriter.flush();  //刷新缓冲区,可能导致客户端不能第一时间看到响应System.out.printf("[%s:%d] req: %s,resp:%s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,response);}}}catch (IOException e) {e.printStackTrace();}finally {try {clientSocket.close();  //关闭操作}catch (IOException e) {e.printStackTrace();}}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpThreadEpochServer server = new TcpThreadEpochServer(9090);server.start();}
}

3.2 翻译服务器

客户端内容不做改动,只需对服务器中的process方法改动即可(实现翻译的逻辑),让翻译客户端继承自上面的客户端即可。

import java.io.IOException;
import java.util.HashMap;public class TcpDictServer extends TcpEpochServer{  //继承之后重写process方法private HashMap map = new HashMap<>();  //创建一个HashMap用于接收翻译之间的映射关系public TcpDictServer(int port) throws IOException {super(port);map.put("cat","猫咪");map.put("dog","小狗");map.put("pig","小猪");map.put("apple","苹果");map.put("banana","香蕉");}@Overridepublic String process(String request) {return map.getOrDefault(request,"当前语料库不足以翻译此词条");}public static void main(String[] args) throws IOException {TcpDictServer server = new TcpDictServer(9090);server.start();}
}

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
平遥古城在哪 平遥古城基本现状 城垣恢宏,雉堞连云市井街巷,店肆林立四合院落,青砖瓦屋这里是平遥古城历经千年沧桑而古朴宛然,风姿依旧...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
平遥古城在哪 平遥古城基本现状 城垣恢宏,雉堞连云市井街巷,店肆林立四合院落,青砖瓦屋这里是平遥古城历经千年沧桑而古朴宛然,风姿依旧...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...