文章目录

  • 前言
  • 一对一单次通信
  • 一对一多次通信
  • 一对多多次通信
  • 总结
  • UDP通信

前言

  现如今,互联网几乎在我身边随处可见,早已深入我们人类生活中。如今的软件如果没有网络功能,那是很不可思议的事情。(单机游戏都有局域网功能,小时候和朋友联机打的红警就是例子)。许多语言都支持网络编程,我见过C与Java有关网络的程序代码,从中深刻感受到有工具和没工具编代码效率是多么天差地别。就相当于在赛跑,C还在那里造车轮子,Java已经开始用别人给的车开始跑了。

  最近一直在学习Java,因此用Java学习网络编程。C/S模式,又称客户端服务器模式。至少用两台计算机来分别充当客户机和服务器角色。C/S模式将应用(对于客户端来说)与服务(对于服务器来说)分离,两边各自开发自己的,两套程序。对于服务器,Java提供一个ServerSocket类来创建服务器,对于客户端,Java提供一个Socket类可以连接服务器。

  接下来,就先写三个简单的网络通信的例子(一对一单次通信和一对一多次通信)来熟悉下相关类。

一对一单次通信

 一对一单次通信:一个服务器和一个客户端单次通信

服务器

public class OneToOneServer {public static void main(String[] args) {int port = 54188;try {System.out.println("开始启动服务器...");ServerSocket server = new ServerSocket(port);System.out.println("服务器启动成功!");System.out.println("开始侦听客户端连接请求...");Socket client = server.accept();InetAddress inetaddress = client.getInetAddress();String clientIp = inetaddress.getHostAddress();    //客户端ip地址String clientName = inetaddress.getHostName(); //客户端名称System.out.println("发现客户端(ip: " + clientIp   + ", 主机名:" + clientName + ")请求连接服务器");DataInputStream dis = new DataInputStream(client.getInputStream());  //开始建立通信信道DataOutputStream dos = new DataOutputStream(client.getOutputStream());System.out.println("通信信道已建立完成");String message = dis.readUTF();System.out.println("来自客户端的消息[" + message + "]");dos.writeUTF("我收到你的消息了");dis.close();dos.close();client.close();server.close();} catch (IOException e) {e.printStackTrace();}}
}

客户端

public class OneToOneClient {public static void main(String[] args) {String serverIp = "127.0.0.1";   //服务器ip地址,因为本地测试所以127.0.0.1int serverPort = 54188;//和服务器协商好的try {System.out.println("开始连接服务器.....");Socket socket = new Socket(serverIp, serverPort);System.out.println("连接成功");DataInputStream dis = new DataInputStream(socket.getInputStream());DataOutputStream dos = new DataOutputStream(socket.getOutputStream());dos.writeUTF("你好,我是一个客户端");String message = dis.readUTF();System.out.println("来自服务器的消息:" + message);dis.close();dos.close();socket.close();} catch (UnknownHostException e) {          //服务器名称或地址不存在。未知主机异常e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}

运行结果

#服务器
开始启动服务器...
服务器启动成功!
开始侦听客户端连接请求...
发现客户端(ip: 127.0.0.1, 主机名:127.0.0.1)请求连接服务器
通信信道已建立完成
来自客户端的消息[你好,我是一个客户端]#客户端
开始连接服务器.....
连接成功
来自服务器的消息:我收到你的消息了

从上面一对一通信程序,希望你能了解到以下几点。

服务器方面:

  • ServerSocket的建立需要port(端口号),且这个端口号必须和连接的客户端保持一直,也就是说要和客户端协商好。
  • 如果没有客户端连接,程序会卡在accept()那个地方,可以类似于scanf函数,若没有客户端连接则程序会停在accept()那个地方,有客户端连接才会让程序向下进行
  • 可以让客户端先不发消息(把发消息那里注释掉),观察情况,发现两个程序都卡住了,都卡在dis.readUTF()这里。所以dis.readUTF()这个方法也是类似于scanf函数,没有收到对端的消息就卡在这,程序不往下走。dis.readUTF()这个方法是读对端发来的消息,是被动的等,一直等,没有就停在这里
  • dos.writeUTF()相当于给对端发消息,主动的发消息。

客户端方面:

  • Socket的建立需要服务器的ip地址和和服务器协商好的port
  • 也需要建立DataInputStream和DataOutputStream

总结一下服务器和客户端都需要DataInputStream和DataOutputStream。它俩相当于什么呢?可以这么说就相当于我们学的通信原理里面的信道那个概念,两端进行信息交换的通信信道;也可以举个现实生活中的例子,就是我们的座机电话,他有两端,上面是听筒端(DataInputStream)和说话端(DataOutputStream)。

一对一多次通信

  一对一多次通信:一个服务器和一个客户端多次通信

  上述一对一单次通信的代码很简单,我们要从中熟悉一些需要用的类。但是,我相信肯定不是上面我们最终的目的吧?不可能通信只通信一句话吧?所以来进一步实现一个客户端和一个服务器多次通信。

  如何进行多次通信呢?经过思考只要通信信道不关闭,就可以多次发送消息。但不能一直通信下去,要有办法停止,也就是可控性。所以定发特定的字符串就停止下来。

服务器

public class SimpleServer {private int port;private ServerSocket serverSocket;private DataInputStream dis;private DataOutputStream dos;private Socket client;public SimpleServer() throws IOException {port = 54188;System.out.println("开始建立服务器");serverSocket = new ServerSocket(port);System.out.println("服务器建立完成");}private void close() {try {if (dis != null) {dis.close();}} catch (IOException e) {e.printStackTrace();} finally {dis = null;}try {if (dos != null) {dos.close();}} catch (IOException e) {e.printStackTrace();} finally {dos = null;}try {if (client != null && !client.isClosed()) {client.close();}} catch (IOException e) {e.printStackTrace();} finally {client = null;}try {if (serverSocket != null && !serverSocket.isClosed()) {serverSocket.close();}} catch (IOException e) {e.printStackTrace();} finally {serverSocket = null;}}public void listeningClient() throws IOException {System.out.println("开始侦听客户端连接请求");client = serverSocket.accept();System.out.println("客户端[" + client.getInetAddress().getHostAddress() + ", " + client.getInetAddress().getHostName()+ "]连入服务器");dis = new DataInputStream(client.getInputStream());dos = new DataOutputStream(client.getOutputStream());String message = "";System.out.println("接受来自客户端的消息...");while (!message.equalsIgnoreCase("byebye")) {message = dis.readUTF();System.out.println("来自客户端的消息:" + message);dos.writeUTF("[" + message + "]");}close();}}

客户端

public class SimpleClient {private Socket socket;private String ip;private int port;private DataInputStream dis;private DataOutputStream dos;public SimpleClient() throws UnknownHostException, IOException {ip = "127.0.0.1";port = 54188;System.out.println("开始连接服务器...");socket = new Socket(ip, port);System.out.println("连接服务器成功");dis = new DataInputStream(socket.getInputStream());dos = new DataOutputStream(socket.getOutputStream());}public void talking() {Scanner in = new Scanner(System.in);String message = "";String messageFromServer;try {while(!message.equalsIgnoreCase("byebye")) {message = in.nextLine();dos.writeUTF(message);messageFromServer = dis.readUTF();System.out.println("来自服务器的消息:" +messageFromServer);}} catch (IOException e) {e.printStackTrace();}in.close();close();}private void close() {try {if (dis != null) {dis.close();}} catch (IOException e) {e.printStackTrace();} finally {dis = null;}try {if (dos != null) {dos.close();}} catch (IOException e) {e.printStackTrace();} finally {dos = null;}try {if (socket != null && !socket.isClosed()) {socket.close();}} catch (IOException e) {e.printStackTrace();} finally {socket = null;}}
}

运行代码。注意:要先运行服务器,在运行客户端

public class Test {public static void main(String[] args) {try {new SimpleServer().listeningClient();} catch (IOException e) {e.printStackTrace();}}}public class Test {public static void main(String[] args) {try {new SimpleClient().talking();} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}

显示结果如下

#服务器
开始建立服务器
服务器建立完成
开始侦听客户端连接请求
客户端[127.0.0.1, 127.0.0.1]连入服务器
接受来自客户端的消息...
来自客户端的消息:你好
来自客户端的消息:我是一个客户端
来自客户端的消息:byebye#客户端
开始连接服务器...
连接服务器成功
你好
来自服务器的消息:[你好]
我是一个客户端
来自服务器的消息:[我是一个客户端]
byebye
来自服务器的消息:[byebye]

一对多多次通信

  一对多多次通信:一个服务器和多个客户端的多次通信

  前面的代码不是我们最终的目的,如果一个服务器只能接入一个客户端,你说他还能称为服务器吗?服务器作为网络节点中心管理者,要实现接入多个客户端的功能。经过分析上述代码得知之所以只接入了一个客户端的原因是,accept()方法只运行了一次,要想接入多个客户端,必须让accept()方法重复执行,我们可以把它放在循环里,条件是服务器关闭与否。但是还是有问题,前面说过accept()方法相当于scanf函数,没有接入它的客户端,程序会卡在那里阻止下面通信的代码。解决方法:用线程。服务器accept()侦听客户端连接用线程并行去跑,不要影响我下面的代码。

  同时分析上述代码我们可以知道每连接一个新的客户端都要新造一个独立的通信信道,这是当然啦,你要和服务器建立联系,就得造通信信道,该通信信道是只有你和服务器用的,独立的,不能让别人用。两端都要有通信信道(DataInputStream和DataOutputStream)所以我们最好把它单独拎出来做成一个类。同时我们知道dis.read()方法也相当于scanf函数,程序卡在这里不会运行以后的代码,你等待对端消息不能干扰我主动给对端发消息吧。因此,也用线程解决。

  只有做到以上两点,才能完成一对多多次通信。才能实现服务器边听(侦听客户端连接)边说(给对端发消息)!可以用以下图示来说明。

先写双方都需要建立的通信信道类Communcation

public abstract class Communcation implements Runnable {private Socket socket;private DataInputStream dis;private DataOutputStream dos;private volatile boolean goon;    //volatitle关键字:拒绝内存优化,这是线程安全相关问题public Communcation(Socket socket) {this.socket = socket;try {dis = new DataInputStream(socket.getInputStream());dos = new DataOutputStream(socket.getOutputStream());goon = true;new Thread(this, "通信层").start();} catch (IOException e) {e.printStackTrace();}}void send(String message) {try {dos.writeUTF(message);} catch (IOException e) {close();}}protected abstract void dealPeerMessage(String message);protected abstract void dealPeerAbnoramlDrop();@Overridepublic void run() {String message = null;while (goon) {try {message = dis.readUTF();dealPeerMessage(message); //处理对端消息具体做法让外面来决定} catch (IOException e) {if (goon == true) {dealPeerAbnoramlDrop(); //当goon还是true的时候,说明是对端异常掉线,做法让外面来决定}close();}}close();}void close() {goon = false;try {if (socket != null && !socket.isClosed()) {socket.close();}} catch (IOException e) {e.printStackTrace();} finally {socket = null;}try {if (dis != null) {dis.close();} } catch (IOException e) {e.printStackTrace();} finally {dis = null;}try {if (dos != null) {dos.close();}  } catch (IOException e) {e.printStackTrace();} finally {dos = null;}}
}

服务器

public class MulServer implements Runnable {private ServerSocket server;private int port;private boolean goon;public MulServer() {port = 54188;}public void startUp() throws IOException {if (goon == true) {System.out.println("服务器已经启动,请勿重复启动");return;}System.out.println("开始建立服务器...");server = new ServerSocket(port);System.out.println("服务器建立成功");goon = true;System.out.println("开始侦听客户端连接...");new Thread(this, "服务器").start();}public void shutDown() {if (goon == false) {System.out.println("服务器已经宕机,请勿重复宕机");return;}close();System.out.println("服务器正常宕机");}public boolean isStartup() {return goon;}@Overridepublic void run() {while (goon) {try {Socket client = server.accept();System.out.println("客户端[" + client.getInetAddress().getHostAddress() + client.getInetAddress().getHostName() + "]连入服务器");new Communcation(client) {@Overrideprotected void dealPeerMessage(String message) {if (message.equalsIgnoreCase("byebye")) {System.out.println("客户端正常下线");close();return;}System.out.println("收到来自客户端的消息:" + message);send("[" + message +"]");}@Overrideprotected void dealPeerAbnoramlDrop() {System.out.println("客户端异常掉线");}};} catch (IOException e) {close();}}}private void close() {goon = false;try {if (server != null && !server.isClosed()) {server.close();}} catch (IOException e) {e.printStackTrace();} finally {server = null;}}
}

客户端

public class MulClient {private Socket socket;private int port;private String serverIp;private Communcation communcation;public MulClient() {port = 54188;serverIp = "127.0.0.1";}public void send(String message) {if (communcation == null) {return;}communcation.send(message);}public void close() {if (communcation == null) {return;}communcation.close();}public void connectToServer() throws UnknownHostException, IOException {socket = new Socket(serverIp, port);communcation = new Communcation(socket) {@Overrideprotected void dealPeerMessage(String message) {System.out.println("来自服务器的消息:" + message);}@Overrideprotected void dealPeerAbnoramlDrop() {System.out.println("服务器异常宕机,服务停止");}};}}

服务器测试

public class ServerTest {public static void main(String[] args) {MulServer mulServer = new MulServer();Scanner in = new Scanner(System.in);String command = null;boolean finshed = false;while (!finshed) {command = in.next();if (command.equalsIgnoreCase("st")) {try {mulServer.startUp();} catch (IOException e) {e.printStackTrace();}} else if (command.equalsIgnoreCase("sd")) {mulServer.shutDown();} else if (command.equalsIgnoreCase("x")) {if (!mulServer.isStartup()) {finshed = true;} else {System.out.println("服务器尚未宕机!");}}}in.close();}}

客户端测试

public class ClientTest {public static void main(String[] args) {MulClient client = new MulClient();boolean finished = false;try {client.connectToServer();Scanner in = new Scanner(System.in);String message = "";while (!finished) {message = in.next();client.send(message);if (message.equalsIgnoreCase("byebye")) {client.close();finished = true;}}in.close();} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}

先运行服务器测试代码,在运行客户端测试代码。进行测试结果如下

#服务器
st
开始建立服务器...
服务器建立成功
开始侦听客户端连接...
客户端[127.0.0.1127.0.0.1]连入服务器
客户端[127.0.0.1127.0.0.1]连入服务器
客户端[127.0.0.1127.0.0.1]连入服务器
收到来自客户端的消息:123
收到来自客户端的消息:456
收到来自客户端的消息:789
客户端正常下线
客户端异常掉线
客户端正常下线
sd
服务器正常宕机
x#客户端1
123
来自服务器的消息:[123]
byebye
#客户端2 故意强制关闭
456
来自服务器的消息:[456]#客户端3
789
来自服务器的消息:[789]
byebye

总结

  一对多多次通信就完成了。服务器可以接入多个客户端,客户端可以发消息给服务器。但是,这就完了吗?这就是我们想要的吗?我并不认为就完了,我们做的功能太简陋了,完全不够用。同时思考可不可以将底层通信的代码做成工具,做成普适性工具,这样以后开发不同需求的C/S程序就会方便很多。这就是框架的含义,也就是我们要做的工具CSFramework。

【Java框架】保姆级教你写出简易框架——CSFramework

UDP通信

  前面的方式是TCP,像打电话,需要建立通信信道,只有两边通信信道都建立好了才可以进行信息的交互。而下面要说的是UDP,像发信息,不需要经过连接,需要知道对方的ip和port。
  需要两个类完成UDP传输。DatagramPacket和DatagramSocket。

发送方代码

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;public class UdpSenderDemo {public static void main(String[] args) {try {DatagramSocket sender = new DatagramSocket();    //发送方直接newbyte[] buffer = new byte[1024];String message = "你好,我是一个udp包,你可以收到吗?";buffer = message.getBytes();DatagramPacket data = new DatagramPacket(buffer, 0, buffer.length, InetAddress.getLocalHost(), 54188);//发送时要把发送的ip和port说清楚sender.send(data);sender.close();} catch (SocketException e) {e.printStackTrace();} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}

其直接运行都不会出错,更加说明了发UDP包不需要连接,我只管发送,发送完后,到没到我都不知道,只管发。

接收方代码

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class UdpReciverDemo {public static void main(String[] args) {try {DatagramSocket reciver = new DatagramSocket(54188); //接收方实例化需要开自己的portbyte[] buffer = new byte[1024];DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);//接收时,不需要ip和port,直接接受网络消息到缓冲区即可reciver.receive(packet);                //阻塞接受System.out.println(new String(packet.getData(), 0, packet.getLength()));reciver.close();} catch (SocketException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}

从这个例子明白了没有客户端和服务器之分,都是DatagramScoket。叫发送端和接收端。

【Java基础】Java网络编程基础相关推荐

  1. java程序设计之网络编程基础教程_Java程序设计之网络编程基础教程

    基本信息 书名:Java程序设计之网络编程基础教程(21世纪高等学校计算机基础实用规划教材) :43.50元 作者:李芝兴 主编 出版社:清华大学出版社 出版日期:2012-12-1 ISBN:978 ...

  2. Java网络编程基础_Java网络编程基础篇

    一.前言 网络通讯在系统交互中是必不可少的一部分,无论是面试还是工作中都是绕不过去的一部分,本节我们来谈谈Java网络编程中的一些知识,本chat内容如下: 网络通讯基础知识,剖析网络通讯的本质和需要 ...

  3. 第一章 网络编程基础

    网络编程基础 1. 网络编程基础概念 对于C/C++编程人员来说,网络编程的本质实际就是使用系统提供的网络编程接口,完成应用程序的网络数据收发功能. 2. 网络编程核心概念 网络编程中一般我们会经常听 ...

  4. 迈入JavaWeb第一步,Java网络编程基础,TCP网络编程URL网络编程等

    文章目录 网络编程概述 网络通信要素 要素一IP和端口号 要素二网络协议 TCP网络编程 UDP网络编程 URL网络编程 Java网络编程基础 网络编程概述 Java是Internet上的语言,它从语 ...

  5. java 编程原理_Java网络编程 -- 网络编程基础原理

    Hello,今天记录下 Java网络编程 --> 网络编程基础原理. 一起学习,一起进步.继续沉淀,慢慢强大.希望这文章对您有帮助.若有写的不好的地方,欢迎评论给建议哈! 初写博客不久,我是杨展 ...

  6. JAVA基础11 网络编程

    JAVA基础 11.网络编程 1.什么是网络?网络模型?网络四要素? 1.网络 在计算机领域中网络是信息传输,接收,共享的虚拟平台,通过它把各个点,面,体的联系到一起,从而实现这些资源的共享. 资源的 ...

  7. Java网络编程和NIO详解开篇:Java网络编程基础

    老曹眼中的网络编程基础 转自:https://mp.weixin.qq.com/s/XXMz5uAFSsPdg38bth2jAA 我们是幸运的,因为我们拥有网络.网络是一个神奇的东西,它改变了你和我的 ...

  8. Java 网络编程基础知识

    Java 网络编程基础知识 基础概念 计算机网络的基本概念 ​ 网络:多台计算机使用网络设备互联在一起,计算机之间可以进行通信,这样就组成了一个计算机网络. ​ 网络设备:集线器(HUB),路由器,交 ...

  9. [Java入门笔记] 面向对象编程基础(二):方法详解

    2019独角兽企业重金招聘Python工程师标准>>> 什么是方法? 简介 在上一篇的blog中,我们知道了方法是类中的一个组成部分,是类或对象的行为特征的抽象. 无论是从语法和功能 ...

  10. python网络编程基础(线程与进程、并行与并发、同步与异步、阻塞与非阻塞、CPU密集型与IO密集型)...

    python网络编程基础(线程与进程.并行与并发.同步与异步.阻塞与非阻塞.CPU密集型与IO密集型) 目录 线程与进程并行与并发同步与异步阻塞与非阻塞CPU密集型与IO密集型 线程与进程 进程 前言 ...

最新文章

  1. oracle celient 作用,WebLogic Server 10.3 SSL配置及SSL协议传输的WebSevice调用.doc
  2. PHP 项目中单独使用 Laravel Eloquent 查询语句来避免 SQL 注入
  3. nsqlookupd:高性能消息中间件 NSQ 解析
  4. aes c语言 逆列混合函数,c语言aes列混合和逆列混合的实现(3页)-原创力文档
  5. Yii 多表关联relations
  6. ubuntu16.04 安装完显卡驱动后分辨率固定640x480 解决
  7. DB2 8.2 9.1 9.5 9.7 下载地址(原创)
  8. Android直播带货app源码——直播推流SDK
  9. 半导体PN结的工作原理
  10. 后缀树后缀数组LCP
  11. MCS9865串口卡并口卡驱动
  12. 硬石类山水盆景的制造技法
  13. 微信公众号开发---生成带参二维码
  14. MOOC中国大学自动评分js脚本
  15. 敏捷开发绩效管理之四:为团队设立外部绩效目标(目标管理,外向型绩效)
  16. storm部署安装deploy
  17. 河北钢铁的数字化雄心
  18. ILPD(印度肝病患者)分类BP算法和KNN
  19. 2 Oracle深度学习笔记 内存架构之UGA
  20. xposed android L主题,小米Note顶配版 LineageOS14.1(原CM) 安卓7.1.2 Xposed框架+主题+号码归属地-刷机之家...

热门文章

  1. matlab 拉普拉斯金字塔,图像拉普拉斯金字塔(laplacian pyramid)并从金字塔重建原图 matlab程序实现...
  2. 查看计算机上硬盘的大小,怎么看硬盘的缓存信息?电脑硬盘缓存大小查看方法图解...
  3. 过去分词做宾语补足语
  4. 一图学会配置微信云端店员监控收款回调
  5. 美国计算机一年制,美国一年制硕士
  6. 伍斯特理工学院计算机,伍斯特理工大学
  7. 斯蒂文理工计算机排名,全美最强STEM大学排行榜发布!这些学校考虑一下?
  8. linux下载navicat
  9. Matlab帮助文档打开和命令窗口中文显示设置
  10. HBuilder 第一个app项目