一、网络编程基础知识

计算机网络是指两台或更多的计算机组成的网络,在同一个网络中,任意两台计算机都可以直接通信,因为所有计算机都需要遵循同一种网络协议。
那什么是互联网呢?互联网是网络的网络(internet),即把很多计算机网络连接起来,形成一个全球统一的互联网。
对某个特定的计算机网络来说,它可能使用网络协议ABC,而另一个计算机网络可能使用网络协议XYZ。如果计算机网络各自的通讯协议不统一,就没法把不同的网络连接起来形成互联网。因此,为了把计算机网络接入互联网,就必须使用TCP/IP协议。
TCP/IP协议泛指互联网协议,其中最重要的两个协议是TCP协议和IP协议。只有使用TCP/IP协议的计算机才能够联入互联网,使用其他网络协议(例如NetBIOS、AppleTalk协议等)是无法联入互联网的。

1.IP地址

在互联网中,一个IP地址用于唯一标识一个网络接口(Network Interface)。一台联入互联网的计算机肯定有一个IP地址,但也可能有多个IP地址。
IP地址分为IPv4和IPv6两种。IPv4采用32位地址,类似101.202.99.12,而IPv6采用128位地址,类似2001:0DA8:100A:0000:0000:1020:F2F3:1428。IPv4地址总共有232个(大约42亿),而IPv6地址则总共有2128个(大约340万亿亿亿亿),IPv4的地址目前已耗尽,而IPv6的地址是根本用不完的。
IP地址又分为公网IP地址和内网IP地址。公网IP地址可以直接被访问,内网IP地址只能在内网访问。内网IP地址类似于:

  • 192.168.x.x
  • 10.x.x.x

有一个特殊的IP地址,称之为本机地址,它总是127.0.0.1
IPv4地址实际上是一个32位整数。例如:

  1. 106717964 = 0x65ca630c
  2. = 65 ca 63 0c
  3. = 101.202.99.12

如果一台计算机只有一个网卡,并且接入了网络,那么,它有一个本机地址127.0.0.1,还有一个IP地址,例如101.202.99.12,可以通过这个IP地址接入网络。
如果一台计算机有两块网卡,那么除了本机地址,它可以有两个IP地址,可以分别接入两个网络。通常连接两个网络的设备是路由器或者交换机,它至少有两个IP地址,分别接入不同的网络,让网络之间连接起来。
如果两台计算机位于同一个网络,那么他们之间可以直接通信,因为他们的IP地址前段是相同的,也就是网络号是相同的。网络号是IP地址通过子网掩码过滤后得到的。例如:
某台计算机的IP是101.202.99.2,子网掩码是255.255.255.0,那么计算该计算机的网络号是:

  1. IP = 101.202.99.2
  2. Mask = 255.255.255.0
  3. Network = IP & Mask = 101.202.99.0

每台计算机都需要正确配置IP地址和子网掩码,根据这两个就可以计算网络号,如果两台计算机计算出的网络号相同,说明两台计算机在同一个网络,可以直接通信。如果两台计算机计算出的网络号不同,那么两台计算机不在同一个网络,不能直接通信,它们之间必须通过路由器或者交换机这样的网络设备间接通信,我们把这种设备称为网关。
网关的作用就是连接多个网络,负责把来自一个网络的数据包发到另一个网络,这个过程叫路由。
所以,一台计算机的一个网卡会有3个关键配置:
9、网络编程 - 图1

  • IP地址,例如:10.0.2.15
  • 子网掩码,例如:255.255.255.0
  • 网关的IP地址,例如:10.0.2.2

    2.域名

    因为直接记忆IP地址非常困难,所以我们通常使用域名访问某个特定的服务。域名解析服务器DNS负责把域名翻译成对应的IP,客户端再根据IP地址访问服务器。
    nslookup可以查看域名对应的IP地址:

    1. $ nslookup www.liaoxuefeng.com
    2. Server: xxx.xxx.xxx.xxx
    3. Address: xxx.xxx.xxx.xxx#53
    4. Non-authoritative answer:
    5. Name: www.liaoxuefeng.com
    6. Address: 47.98.33.223

    有一个特殊的本机域名localhost,它对应的IP地址总是本机地址127.0.0.1

    3.网络模型

    由于计算机网络从底层的传输到高层的软件设计十分复杂,要合理地设计计算机网络模型,必须采用分层模型,每一层负责处理自己的操作。OSI(Open System Interconnect 开放式系统互联)网络模型是ISO组织定义的一个计算机互联的标准模型,注意它只是一个定义,目的是为了简化网络各层的操作,提供标准接口便于实现和维护。这个模型从上到下依次是:

  • 应用层,提供应用程序之间的通信;

  • 表示层:处理数据格式,加解密等等;
  • 会话层:负责建立和维护会话;
  • 传输层:负责提供端到端的可靠传输;
  • 网络层:负责根据目标地址选择路由来传输数据;
  • 链路层和物理层负责把数据进行分片并且真正通过物理网络传输,例如,无线网、光纤等。

互联网实际使用的TCP/IP模型并不是对应到OSI的7层模型,而是大致对应OSI的5层模型:
9、网络编程 - 图29、网络编程 - 图3

  • TCP(Transmission Control Protocol)传输控制协议
    传输基本单位是报文段

应用范围:运输层,进程之间

  • UDP(User Datagram Protocol)用户数据报协议
    传输基本单位是用户数据报

应用范围:运输层,不同主机上的进程之间

  • IP(Internet Protocol)协议
    用于网络层,主要为不同的主机之间的数据传输提供服务

服务与协议之间的关系
协议是对等的,服务是垂直的。
协议的作用是保证不同实体之间的传输是对等的,而不同层之间,通过“服务”来使下层给上层暴露出有限的接口,即层间接口,使得上层能够使用下层的服务,来完成协议的功能
参考:https://www.cnblogs.com/bqwzx/p/11053778.html

4.常用协议

IP协议是一个分组交换,它不保证可靠传输。而TCP协议是传输控制协议,它是面向连接的协议,支持可靠传输和双向通信。TCP协议是建立在IP协议之上的,简单地说,IP协议只负责发数据包,不保证顺序和正确性,而TCP协议负责控制数据包传输,它在传输数据之前需要先建立连接,建立连接后才能传输数据,传输完后还需要断开连接。TCP协议之所以能保证数据的可靠传输,是通过接收确认、超时重传这些机制实现的。并且,TCP协议允许双向通信,即通信双方可以同时发送和接收数据。
TCP协议也是应用最广泛的协议,许多高级协议都是建立在TCP协议之上的,例如HTTP、SMTP等。
UDP协议(User Datagram Protocol)是一种数据报文协议,它是无连接协议,不保证可靠传输。因为UDP协议在通信前不需要建立连接,因此它的传输效率比TCP高,而且UDP协议比TCP协议要简单得多。
选择UDP协议时,传输的数据通常是能容忍丢失的,例如,一些语音视频通信的应用会选择UDP协议。

5.小结

计算机网络的基本概念主要有:

  • 计算机网络:由两台或更多计算机组成的网络;
  • 互联网:连接网络的网络;
  • IP地址:计算机的网络接口(通常是网卡)在网络中的唯一标识;
  • 网关:负责连接多个网络,并在多个网络之间转发数据的计算机,通常是路由器或交换机;
  • 网络协议:互联网使用TCP/IP协议,它泛指互联网协议簇;
  • IP协议:一种分组交换传输协议;
  • TCP协议:一种面向连接,可靠传输的协议;
  • UDP协议:一种无连接,不可靠传输的协议。

    二、TCP通信

    TCP通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)与服务端(Server)。
    两端通信时步骤:
  1. 服务端程序,需要事先启动,等待客户端的连接。
  2. 客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连接客户端。

在Java中,提供了两个类用于实现TCP通信程序:

  1. 客户端:java.net.Socket 类表示。创建Socket对象,向服务端发出连接请求,服务端响应请求,两者建立连接开始通信。
  2. 服务端:java.net.ServerSocket 类表示。创建ServerSocket对象,相当于开启一个服务,并等待客户端的连接。

    1.Socket类

    构造方法

  • public Socket(String host, int port) :创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的host是null ,则相当于指定地址为回送地址。

    小贴士:回送地址(127.x.x.x) 是本机回送地址(Loopback Address),主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,立即返回,不进行任何网络传输。

构造举例,代码如下:

  1. Socket client = new Socket("127.0.0.1", 6666);

成员方法

  • public InputStream getInputStream() : 返回此套接字的输入流。
    • 如果此Scoket具有相关联的通道,则生成的InputStream 的所有操作也关联该通道。
    • 关闭生成的InputStream也将关闭相关的Socket。
  • public OutputStream getOutputStream() : 返回此套接字的输出流。
    • 如果此Scoket具有相关联的通道,则生成的OutputStream 的所有操作也关联该通道。
    • 关闭生成的OutputStream也将关闭相关的Socket。
  • public void close() :关闭此套接字。
    • 一旦一个socket被关闭,它不可再使用。
    • 关闭此socket也将关闭相关的InputStream和OutputStream 。
  • public void shutdownOutput() : 禁用此套接字的输出流。

    • 任何先前写出的数据将被发送,随后终止输出流。

      2.ServerSocket类

      ServerSocket类:这个类实现了服务器套接字,该对象等待通过网络的请求。

      构造方法

  • public ServerSocket(int port) :使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上,参数port就是端口号。

构造举例,代码如下:

  1. ServerSocket server = new ServerSocket(6666);

成员方法

  • public Socket accept() :侦听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法会一直阻塞直到建立连接。

    3.TCP通信分析图解

  1. 【服务端】启动,创建ServerSocket对象,等待连接。
  2. 【客户端】启动,创建Socket对象,请求连接。
  3. 【服务端】接收连接,调用accept方法,并返回一个Socket对象。
  4. 【客户端】Socket对象,获取OutputStream,向服务端写出数据。
  5. 【服务端】Scoket对象,获取InputStream,读取客户端发送的数据。

    到此,客户端向服务端发送数据成功。

9、网络编程 - 图4

自此,服务端向客户端回写数据。

  1. 【服务端】Socket对象,获取OutputStream,向客户端回写数据。
  2. 【客户端】Scoket对象,获取InputStream,解析回写数据。
  3. 【客户端】释放资源,断开连接。

服务器端实现
9、网络编程 - 图5

  1. public class ServerTcp {
  2. public static void main(String[] args) throws IOException {
  3. //1.创建服务端对象
  4. ServerSocket ss = new ServerSocket(7788);
  5. //2.监听客户端连接,并获得套接字对象
  6. System.out.println("等待客户端连接>>>");
  7. Socket s = ss.accept();
  8. //3.获得输入流,读数据,并把数据显示在控制台
  9. InputStream is = s.getInputStream();
  10. //int len = read(byte[] b) 从输入流读取字节数,并将它们存储到缓冲区 b,返回输入流字节长度
  11. byte[] bys = new byte[1024];
  12. int len = is.read(bys);
  13. String data = new String(bys,0,len);
  14. System.out.println("服务端接收到的数据是:"+data);
  15. //3.获得输出流向客户端发送数据
  16. OutputStream outputStream = s.getOutputStream();
  17. outputStream.write(data.toUpperCase().getBytes());
  18. //4.释放资源
  19. s.close(); //可以不用写
  20. ss.close();
  21. }
  22. }

客户端实现:
9、网络编程 - 图6

  1. public class ClientTcp {
  2. public static void main(String[] args) throws IOException {
  3. //1.创建socket对象
  4. Socket s = new Socket("127.0.0.1",7788);
  5. //2.获得输出流,写数据
  6. OutputStream outputStream = s.getOutputStream();
  7. outputStream.write("我是tcp客户端,我非常喜欢java编程".getBytes());
  8. //2.获得输入流,接收数据
  9. InputStream inputStream = s.getInputStream();
  10. byte[] bys = new byte[1024];
  11. int len = inputStream.read(bys);
  12. System.out.println("收到服务端的回复:"+new String(bys,0,len));
  13. //3.释放资源
  14. s.close();
  15. }
  16. }

4.TCP案例练习

练习1

  • 客户端:发送数据,接收服务端反馈
  • 服务端:接收数据,给出反馈

练习2

  • 客户端:数据来自键盘录入,直到输入的数据是886,发送数据结束
  • 服务端:接收到的数据在控制台输出

练习3

  • 客户端:数据来自键盘录入,直到输入的数据是886,发送数据结束
  • 服务端:接收到的数据写入文本文件

练习4(上传文件)

  • 客户端:数据来自文本文件
  • 服务端:接收到的数据写入文本文件

文件上传案例原理分析:
9、网络编程 - 图7
文件上传分析图解:

  1. 【客户端】输入流,从硬盘读取文件数据到程序中。
  2. 【客户端】输出流,写出文件数据到服务端。
  3. 【服务端】输入流,读取文件数据到服务端程序。
  4. 【服务端】输出流,写出文件数据到服务器硬盘中。

9、网络编程 - 图8
客户端实现:

  1. public class UploadClient {
  2. public static void main(String[] args) throws IOException {
  3. //1.
  4. Socket sock = new Socket("127.0.0.1",7788);
  5. //2.准备数据源
  6. BufferedInputStream bis = new BufferedInputStream(new FileInputStream("./1.jpg"));
  7. //3.输出流
  8. BufferedOutputStream bos = new BufferedOutputStream(sock.getOutputStream());
  9. byte[] bys = new byte[1024];
  10. int len=0;
  11. while ((len = bis.read(bys))!= -1){
  12. bos.write(bys,0,len);
  13. bos.flush();
  14. }
  15. //注意:上传成功如果没有结束标记,可以使用:shutdownOutput() 禁用此套接字的输出流。
  16. System.out.println("文件上传成功!");
  17. //4.
  18. sock.close();
  19. }
  20. }

服务器端实现:

  1. public class UploadServer {
  2. public static void main(String[] args) throws IOException {
  3. ServerSocket ss = new ServerSocket(7788);
  4. Socket s = ss.accept();
  5. //输入流-获取客户端发送的数据
  6. BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
  7. //输出流-保存文件到本地
  8. BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("./copy1.jpg"));
  9. //读写数据
  10. byte[] b = new byte[1024];
  11. int len = 0;
  12. while ((len = bis.read(b))!=-1){
  13. bos.write(b,0,len);
  14. bos.flush();
  15. }
  16. System.out.println("服务端收到文件");
  17. //关闭
  18. s.close();
  19. bis.close(); //记得要关闭文件
  20. ss.close();
  21. }
  22. }

文件上传优化分析:

  1. 文件名称写死的问题
    服务端,保存文件的名称如果写死,那么最终导致服务器硬盘,只会保留一个文件,建议使用系统时间优化,保证文件名称唯一,代码如下:
    1. //准备文件夹
    2. File f1 = new File("./upload");
    if(!(f1.exists())){
    f1.mkdirs();
    }
    //不重复的文件名
    String filename = “xjt”+System.currentTimeMillis()+ new Random().nextInt(100)+”.mkv”;
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(f1+”//“+filename)); ```
  2. 循环接收的问题 服务端,指保存一个文件就关闭了,之后的用户无法再上传,这是不符合实际的,使用循环改进,可以不断的接收不同用户的文件,代码如下:

    1. // 每次接收新的连接,创建一个Socket
    2. whiletrue){
    3. Socket accept = serverSocket.accept();
    4. ......
    5. }
  3. 多线程提高效率
    服务端,在接收大文件时,可能耗费几秒钟的时间,此时不能接收其他用户上传,所以,使用多线程技术优化,代码如下:

    1. whiletrue){
    2. Socket accept = serverSocket.accept();
    3. // accept 交给子线程处理.
    4. new Thread(() -> {
    5. ......
    6. InputStream bis = accept.getInputStream();
    7. ......
    8. }).start();
    9. }

    优化实现-服务端

    1. public class UploadServer {
    2. public static void main(String[] args) throws IOException {
    3. ServerSocket ss = new ServerSocket(7788);
    4. while(true){
    5. Socket s = ss.accept();
    6. //线程池
    7. ExecutorService ec = Executors.newFixedThreadPool(10);
    8. ec.submit(() ->{
    9. try{
    10. //输入流-获取客户端发送的数据
    11. BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
    12. //输出流-保存文件到本地
    13. File f1 = new File("./upload");
    14. if(!(f1.exists())){
    15. f1.mkdirs();
    16. }
    17. //文件名称不重复
    18. String filename = "xjt"+System.currentTimeMillis()+ new Random().nextInt(100)+".mkv";
    19. BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(f1+"//"+filename));
    20. //读写数据
    21. byte[] b = new byte[1024];
    22. int len = 0;
    23. while ((len = bis.read(b))!=-1){
    24. bos.write(b,0,len);
    25. bos.flush();
    26. }
    27. System.out.println("服务端收到文件,当前线程是:"+Thread.currentThread().getName());
    28. //关闭
    29. s.close();
    30. bis.close(); //记得要关闭文件
    31. }catch (Exception e){
    32. e.printStackTrace();
    33. }
    34. });
    35. }
    36. //关闭ServerSocket对象
    37. //ss.close(); //这里死循环就不用关了
    38. }
    39. }

    练习5

  • 客户端:数据来自文本文件,接收服务端反馈
  • 服务端:接收到的数据写入文本文件,给出反馈

    出现问题:程序一直等待 原因:读数据的方法是阻塞式的 解决办法:自定义结束标记,使用shutdownOutput() 方法

练习6

  • 客户端:数据来自文本文件,接收服务端反馈
  • 服务端:接收到的数据写入文本文件,给出反馈,代码用线程进行封装,为每一个客户端开启一个线程

9、网络编程 - 图99、网络编程 - 图109、网络编程 - 图11
练习6
模拟网站服务器,使用浏览器访问自己编写的服务端程序,查看网页效果。
案例分析:

  1. 准备页面数据,web文件夹。
    复制到我们Module中,比如复制到day08中
    9、网络编程 - 图12
  2. 我们模拟服务器端,ServerSocket类监听端口,使用浏览器访问
  3. 服务器程序中字节输入流可以读取到浏览器发来的请求信息
    9、网络编程 - 图13

GET/web/index.html HTTP/1.1是浏览器的请求消息。/web/index.html为浏览器想要请求的服务器端的资源,使用字符串切割方式获取到请求的资源。

  1. //转换流,读取浏览器请求第一行
  2. BufferedReader readWb = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  3. String requst = readWb.readLine();
  4. //取出请求资源的路径
  5. String[] strArr = requst.split(" ");
  6. //去掉web前面的/
  7. String path = strArr[1].substring(1);
  8. System.out.println(path);

案例实现
服务端实现:

  1. public class SerDemo {
  2. public static void main(String[] args) throws IOException {
  3. System.out.println("服务端 启动 , 等待连接 .... ");
  4. // 创建ServerSocket 对象
  5. ServerSocket server = new ServerSocket(8888);
  6. Socket socket = server.accept();
  7. // 转换流读取浏览器的请求消息
  8. BufferedReader readWb = new
  9. BufferedReader(new InputStreamReader(socket.getInputStream()));
  10. String requst = readWb.readLine();
  11. // 取出请求资源的路径
  12. String[] strArr = requst.split(" ");
  13. // 去掉web前面的/
  14. String path = strArr[1].substring(1);
  15. // 读取客户端请求的资源文件
  16. FileInputStream fis = new FileInputStream(path);
  17. byte[] bytes= new byte[1024];
  18. int len = 0 ;
  19. // 字节输出流,将文件写会客户端
  20. OutputStream out = socket.getOutputStream();
  21. // 写入HTTP协议响应头,固定写法
  22. out.write("HTTP/1.1 200 OK\r\n".getBytes());
  23. out.write("Content-Type:text/html\r\n".getBytes());
  24. // 必须要写入空行,否则浏览器不解析
  25. out.write("\r\n".getBytes());
  26. while((len = fis.read(bytes))!=-1){
  27. out.write(bytes,0,len);
  28. }
  29. fis.close();
  30. out.close();
  31. readWb.close();
  32. socket.close();
  33. server.close();
  34. }
  35. }

访问效果

  • 火狐

9、网络编程 - 图14

小贴士:不同的浏览器,内核不一样,解析效果有可能不一样。

发现浏览器中出现很多的叉子,说明浏览器没有读取到图片信息导致。
浏览器工作原理是遇到图片会开启一个线程进行单独的访问,因此在服务器端加入线程技术。

  1. public class ServerDemo {
  2. public static void main(String[] args) throws IOException {
  3. ServerSocket server = new ServerSocket(8888);
  4. while(true){
  5. Socket socket = server.accept();
  6. new Thread(new Web(socket)).start();
  7. }
  8. }
  9. static class Web implements Runnable{
  10. private Socket socket;
  11. public Web(Socket socket){
  12. this.socket=socket;
  13. }
  14. public void run() {
  15. try{
  16. //转换流,读取浏览器请求第一行
  17. BufferedReader readWb = new
  18. BufferedReader(new InputStreamReader(socket.getInputStream()));
  19. String requst = readWb.readLine();
  20. //取出请求资源的路径
  21. String[] strArr = requst.split(" ");
  22. System.out.println(Arrays.toString(strArr));
  23. String path = strArr[1].substring(1);
  24. System.out.println(path);
  25. FileInputStream fis = new FileInputStream(path);
  26. System.out.println(fis);
  27. byte[] bytes= new byte[1024];
  28. int len = 0 ;
  29. //向浏览器 回写数据
  30. OutputStream out = socket.getOutputStream();
  31. out.write("HTTP/1.1 200 OK\r\n".getBytes());
  32. out.write("Content-Type:text/html\r\n".getBytes());
  33. out.write("\r\n".getBytes());
  34. while((len = fis.read(bytes))!=-1){
  35. out.write(bytes,0,len);
  36. }
  37. fis.close();
  38. out.close();
  39. readWb.close();
  40. socket.close();
  41. }catch(Exception ex){
  42. }
  43. }
  44. }
  45. }

访问效果:
9、网络编程 - 图15图解:
9、网络编程 - 图16


三、UDP编程

1.UDP发送/接收数据基本步骤

发送:
9、网络编程 - 图179、网络编程 - 图18
接收:
9、网络编程 - 图199、网络编程 - 图20
DatagramPacket构造方法:

  1. DatagramPacket(byte[] buf, int length)
  2. 构造一个 DatagramPacket用于接收长度的数据包 length
  3. DatagramPacket(byte[] buf, int length, InetAddress address, int port)
  4. 构造用于发送长度的分组的数据报包 length指定主机上到指定的端口号。
  5. DatagramPacket(byte[] buf, int offset, int length)
  6. 构造一个 DatagramPacket用于接收长度的分组 length ,指定偏移到缓冲器中。
  7. DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
  8. 构造用于发送长度的分组数据报包 length具有偏移 ioffset指定主机上到指定的端口号。
  9. DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)
  10. 构造用于发送长度的分组数据报包 length具有偏移 ioffset指定主机上到指定的端口号。
  11. DatagramPacket(byte[] buf, int length, SocketAddress address)
  12. 构造用于发送长度的分组的数据报包 length指定主机上到指定的端口号。

DatagramPacket实例对象的方法:

  1. DatagramPacket dp = new DatagramPacket(receive_data,receive_data.length);
  2. dp.getData(); //获得缓冲区数据,字节数据,需要new String() 转为字符串
  3. dp.getLength() //返回要发送的数据的长度或接收到的数据的长度
Modifier and Type Method and Description
InetAddress getAddress() 返回该数据报发送或接收数据报的计算机的IP地址。
byte[] getData() 返回数据缓冲区。
int getLength() 返回要发送的数据的长度或接收到的数据的长度。
int getOffset() 返回要发送的数据的偏移量或接收到的数据的偏移量。
int getPort() 返回发送数据报的远程主机上的端口号,或从中接收数据报的端口号。
SocketAddress getSocketAddress() 获取该数据包发送到或正在从其发送的远程主机的SocketAddress(通常为IP地址+端口号)。
void setAddress(InetAddress iaddr) 设置该数据报发送到的机器的IP地址。
void setData(byte[] buf) 设置此数据包的数据缓冲区。
void setData(byte[] buf, int offset, int length) 设置此数据包的数据缓冲区。
void setLength(int length) 设置此数据包的长度。
void setPort(int iport) 设置发送此数据报的远程主机上的端口号。
void setSocketAddress(SocketAddress address) 设置该数据报发送到的远程主机的SocketAddress(通常是IP地址+端口号)。

练习:
9、网络编程 - 图21
发送:
9、网络编程 - 图22
接收:
9、网络编程 - 图23
写入文本
服务端
9、网络编程 - 图24
客户端:
9、网络编程 - 图25

四、URL

五、发送/接收Email

六、HTTP编程

七、RMI远程调用