socket编程,算是第一次接触吧(⁎⁍̴̛ᴗ⁍̴̛⁎)

理解的有点不是很深刻,所以在此记录一下(>^ω^<)

首先,我们先了解一下网络中的进程之间如何进行通信????

网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。

使用TCP/IP协议的应用程序通常采用应用编程接口socket来实现网络进程之间的通信。

 Socket编程啊~


 

进程间通信的接口是Socket,是实现通信的基础,我们需要知道Socket是什么?????

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。我的理解就是Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)。

现在我们来看看socket函数(只列出我涉及到的一些函数):

  • Socket() 无参构造函数 
  • Socket(InetAddress address, int port) 带有ip地址与端口号的构造函数
  • Socket(String host, int port) 指定ip地址和端口号的构造函数
  • void close() 关闭 你懂的~????
  • InputStream getInputStream()  得到一个输入流(超级有用)
  • OutputStream getOutputStream() 得到一个输出流(超级有用)
  • int getPort()   得到端口号
  • InetAddress getInetAddress() 得到IP地址对象

还有什么函数自己去看API吧,不在赘述!????

 


 

 接下来是什么呢?UDP和TCP!(详见:https://www.cnblogs.com/Allen-rg/p/7190042.html

UDP是什么?User Datagram Protocol 用户数据报协议,位于OSI模型的第四层——传输层。

UDP报文格式

Socket编程啊~

TCP是什么?Transmission Control Protocol 传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。

TCP报文格式

Socket编程啊~


 

UDP和TCP的区别:

 特点 TCP UDP
 是否连接  面向连接  无连接
 传输可靠性  可靠  不可靠
 速度  较慢  快
 应用场合  传输大量数据  传输少量数据

总结:

 1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接

 2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付

 3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的

 4、UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)

 5、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信

7、TCP首部开销20字节;UDP的首部开销小,只有8个字节

 7、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道


 

 UDP的应用????

此处通过案例来讲解

案例一:建立一个发送端,一个接收端。 发送端通过键盘录入的方式发送数据。

 发送端

 1 package review.javase35.udp;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.IOException;
 5 import java.io.InputStreamReader;
 6 import java.net.DatagramPacket;
 7 import java.net.DatagramSocket;
 8 import java.net.InetAddress;
 9 
10 public class UDPSend {
11     public static void main(String[] args) throws IOException {
12         // 创建发送端的服务:
13         DatagramSocket ds = new DatagramSocket(10001); // 给发送端指定的端口号。
14         // 键盘录入:
15         BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
16         String line = null;
17         while ((line = bufr.readLine()) != null) {
18             // 定义一个键盘录入的结束标志:
19             if ("baibai".equals(line)) {
20                 break;
21             }
22             // System.out.println(line);
23             byte buf[] = line.getBytes();
24             DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.1.103"), 9999);
25             // 调用服务的发送方法:
26             ds.send(dp);
27         }
28         // 关闭:
29         ds.close();
30 
31     }
32 }

接收端

 1 package review.javase35.udp;
 2 
 3 import java.io.IOException;
 4 import java.net.DatagramPacket;
 5 import java.net.DatagramSocket;
 6 
 7 public class UDPReceive {
 8     public static void main(String[] args) throws IOException {
 9         // 创建接收端的服务:
10         DatagramSocket ds = new DatagramSocket(9999);
11         while (true) {
12             // 准备空包:
13             byte buf[] = new byte[1024];
14             DatagramPacket dp = new DatagramPacket(buf, buf.length);
15             ds.receive(dp);
16             // 拆包获得数据:
17             String ip = dp.getAddress().getHostAddress();
18             int port = dp.getPort();
19             // 获得数据:
20             byte data[] = dp.getData();
21             System.out.println(new String(data, 0, dp.getLength()));
22         }
23     }
24 }

解析:

  1. DatagramSocket代表UDP协议的Socket,它的唯一作用就是接收和发送数据报。

  2.  DatagramSocket的构造器。

     DatagramSocket():创建一个DatagramSocket实例,并将该对象绑定到本机默认IP地址、本机所有可用端口中随机选择的某个端口。

     DatagramSocket(int prot):创建一个DatagramSocket实例,并将该对象绑定到本机默认IP地址、指定端口。

     DatagramSocket(int port, InetAddress laddr):创建一个DatagramSocket实例,并将该对象绑定到指定IP地址、指定端口。

  3. receive(DatagramPacket p):从该DatagramSocket中接收数据报。

    send(DatagramPacket p):以该DatagramSocket对象向外发送数据报。

  4. 使用DatagramSocket发送数据报时,DatagramSocket并不知道将该数据报发送到哪里,而是由DatagramPacket自身决定数据报的目的地。就像码头并不知道每个集装箱的目的地,码头只是将这些集装箱发送出去,而集装箱本身包含了该集装箱的目的地。

  5. DatagramPacket代表数据报。

  6. DatagramPacket的构造器。

    DatagramPacket(byte[] buf,int length):以一个空数组来创建DatagramPacket对象,该对象的作用是接收DatagramSocket中的数据。

    DatagramPacket(byte[] buf, int length, InetAddress addr, int port):以一个包含数据的数组来创建DatagramPacket对象,创建该DatagramPacket对象时还指定了IP地址和端口--这就决定了该数据报的目的地。

    DatagramPacket(byte[] buf, int offset, int length):以一个空数组来创建DatagramPacket对象,并指定接收到的数据放入buf数组中时从offset开始,最多放length个字节。

    DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port):创建一个用于发送的DatagramPacket对象,指定发送buf数组中从offset开始,总共length个字节。


 

 UDP发送端编程步骤:

  1. 创建发送端的服务 使用DatagramSocket
  2. 创建一个数据包 使用DatagramPacket 将数据以字节数组的形式放入 并指定IP地址和端口号
  3. 发送数据包 send
  4. 关闭服务 

UDP接收端编程步骤:

  1. 创建接收端的服务 使用DatagramSocket
  2. 准备一个空数据包 使用DatagramPacket 封装字节数组 用来接收数据
  3. 接收数据 receive
  4. 拆包获得数据 getData()、  dp.getAddress().getHostAddress() 、 getPort()

 案例二:编写一个程序: 既能实现发送功能,又能实现接收功能: 发送部分和接收部分应该同时执行。 需要使用多线程技术。一个线程控制发送,一个线程接收。

 1 package review.javase35.chat;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.IOException;
 5 import java.io.InputStreamReader;
 6 import java.net.DatagramPacket;
 7 import java.net.DatagramSocket;
 8 import java.net.InetAddress;
 9 import java.net.SocketException;
10 
11 //定义一个发送线程: 
12 class Send implements Runnable {
13     // 将服务作为全局变量引进去:
14     private DatagramSocket ds;
15     // 创建发送端任务的时候 就必须对服务进行初始化。 保证了服务是同一个。
16     public Send(DatagramSocket ds) {
17         this.ds = ds;
18     }
19     @Override
20     public void run() {// run方法: 父类当中没有抛出异常。 那么子类当中一定不能抛。 只能处理。
21         try {
22             // 键盘录入:
23             BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
24             String line = null;
25             while ((line = bufr.readLine()) != null) {
26                 byte buf[] = line.getBytes();
27                 // 创建一个数据包:
28                 DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("127.0.0.1"), 9999);
29                 // 发送端发送数据包
30                 ds.send(dp);
31                 // 定义结束标志:
32                 if (line.equals("886")) {
33                     break;
34                 }
35             }
36         } catch (IOException e) {
37             e.printStackTrace();
38         } finally {
39             ds.close();
40         }
41     }
42 }
43 
44 // 接收端线程:
45 class Receive implements Runnable {
46     // 将服务作为全局变量引进去:
47     private DatagramSocket ds;
48     // 创建发送端任务的时候 就必须对服务进行初始化。 保证了服务是同一个。
49     public Receive(DatagramSocket ds) {
50         this.ds = ds;
51     }
52     @Override
53     public void run() {// run方法: 父类当中没有抛出异常。 那么子类当中一定不能抛。 只能处理。
54         try {
55             while (true) {
56                 byte buf[] = new byte[1024];
57                 // 创建一个数据包:
58                 DatagramPacket dp = new DatagramPacket(buf, buf.length);
59                 // 接收端接收数据包
60                 ds.receive(dp);
61                 // 拆包获得数据:
62                 String ip = dp.getAddress().getHostAddress();
63                 int port = dp.getPort();
64                 String data = new String(dp.getData(), 0, dp.getLength());
65                 System.out.println("ip" + ip + " :" + port + " Data:" + data);
66                 System.out.println(data);
67                 if ("886".equals(data)) {
68                     System.out.println("ip" + ip + "离开聊天室");
69                     break;
70                 }
71             }
72         } catch (IOException e) {
73             e.printStackTrace();
74         }
75     }
76 }
77 public class ChatDemo {
78     public static void main(String[] args) throws SocketException {
79         // 发送端和接收端使用的不是同一个服务。
80         DatagramSocket sendds = new DatagramSocket();
81         DatagramSocket receds = new DatagramSocket(9999);
82         // 创建线程任务对象:
83         Send send = new Send(sendds);
84         Receive rece = new Receive(receds);
85         // 创建线程对象:
86         Thread t1 = new Thread(send);
87         Thread t2 = new Thread(rece);
88         // 启动线程:
89         t1.start();
90         t2.start();
91 
92     }
93 }

解析:结合线程使用 不再赘述~


 

TCP的应用

案例一:客户端发送数据,服务器端能够接收数据。 给客户端发送一个响应数据。  

服务器端

 1 package review.javase35.tcp;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.IOException;
 5 import java.io.InputStream;
 6 import java.io.InputStreamReader;
 7 import java.io.OutputStream;
 8 import java.net.ServerSocket;
 9 import java.net.Socket;
10 
11 public class TcpServer1 {
12     public static void main(String[] args) throws IOException, InterruptedException {
13         /*
14          * 1: 创建服务: ServerSocket,创建服务端的时候需要指定一个监听的端口。 
15          * 2: 获得客户端的服务: accept: 
16          * 3: 通过Socket获得InputStream。 read读取数据。 
17          * 4: 通过Socket获得OutputStream。write() 给客户端响应数据。
18          * 
19          */
20         ServerSocket ss = new ServerSocket(10002);
21         // 获得客户端的服务:
22         Socket s = ss.accept();
23         // 先获得输入流:
24         InputStream in = s.getInputStream();
25         byte b[] = new byte[1024];
26         int num = in.read(b);
27         // 将客户端发送的数据 打印出来。
28         System.out.println(new String(b, 0, num));
29         Thread.sleep(2000);// 服务器端间隔两秒后 再次给客户端响应的数据。
30         // 给客户端响应数据:
31         OutputStream out = s.getOutputStream();
32         out.write("clinet 你好,您发送的数据我收到了".getBytes());
33         s.close();
34         ss.close();
35     }
36 
37 }

客户端

 1 package review.javase35.tcp;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.BufferedWriter;
 5 import java.io.IOException;
 6 import java.io.InputStream;
 7 import java.io.InputStreamReader;
 8 import java.io.OutputStream;
 9 import java.io.OutputStreamWriter;
10 import java.net.Socket;
11 import java.net.UnknownHostException;
12 
13 public class TcpClient1 {
14     public static void main(String[] args) throws UnknownHostException, IOException {
15         /*
16          * 1: 准备客户端的的服务: Socket: 
17          * 2: 获得OutputStream: 写数据给服务器端。 
18          * 3: 准备InputStream:用来读取服务器发送的数据。 
19          * 4: 关闭:
20          */
21         // 准备服务:
22         Socket s = new Socket("localhost", 10002);
23         // 获得输出流:
24         OutputStream out = s.getOutputStream();
25         // 给服务器端写数据:
26         out.write("server 你好吗? ".getBytes());
27         // 准备输入流:
28         InputStream in = s.getInputStream();
29         byte b[] = new byte[1024];
30         // 将服务器端发送过来的数据读取到一个字节数组当中。
31         int num = in.read(b);
32         System.out.println(new String(b, 0, num));
33         // 关闭:
34         s.close();
35     }
36 }

解析

  1. ServerSocket(int port)——创建并绑定到特定端口的服务器套接字
  2. accept()——侦听并接受到此套接字的连接
  3. close()——关闭此套接字
  4. getInetAddress()——得到ServerSocket对象绑定的IP地址。
  5. getLocalPort()——返回此套接字在其上侦听的端口
  6. Socket(InetAddress address, int port)——创建一个套接字并将其连接到指定ip地址的指定端口号
  7. Socket(String host, int port)——创建一个套接字并将其连接到指定主机上的指定端口号
  8. close()——关闭此套接字
  9. getInetAddress()——返回套接字连接的地址
  10. getInputStream()——返回此套接字的输入流
  11. getOutputStream——返回此套接字的输出流

 

TCP服务器端编程步骤

1、创建ServerSocket对象,绑定监听端口

2、通过accept()方法监听客户端请求

3、连接建立后,通过输入流读取客户端发送的请求信息

4、通过输出流向客户端发送响应信息

5、关闭相关资源

TCP客户端编程步骤

1、创建Socket对象,指明需要连接的服务器的地址和端口号

2、连接建立后,通过输出流向服务器端发送请求信息

3、通过输入流获取服务器相应的信息

4、关闭相关资源。


 

案例二:建立一个文本转换器: 客户端给服务器端发送一个文本。服务器端将文本转换成大写返回给客户端。而且客户端可以不断的进行文本准换。当客户端输入over时候,转换结束。

客户端

 1 package review.javase35.tcp;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.BufferedWriter;
 5 import java.io.IOException;
 6 import java.io.InputStream;
 7 import java.io.InputStreamReader;
 8 import java.io.OutputStream;
 9 import java.io.OutputStreamWriter;
10 import java.net.Socket;
11 import java.net.UnknownHostException;
12 
13 public class TextClient {
14     public static void main(String[] args) throws UnknownHostException, IOException {
15         // 准备服务
16         Socket s = new Socket("localhost", 9999);
17         while (true) {
18             // 获得输出流
19             OutputStream out = s.getOutputStream();
20             String inStr = null;
21 
22             BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
23             inStr = br.readLine();
24             out.write(inStr.getBytes());
25             if("over".equals(inStr)) {
26                 break;
27             }
28             // 准备输入流
29             InputStream in = s.getInputStream();
30             byte[] b = new byte[1024];
31             int num=in.read(b);
32             System.out.println(new String(b, 0, num));
33             
34         }
35         s.close();
36     }
37 }

服务端

 1 package review.javase35.tcp;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.BufferedWriter;
 5 import java.io.IOException;
 6 import java.io.InputStream;
 7 import java.io.InputStreamReader;
 8 import java.io.OutputStream;
 9 import java.io.OutputStreamWriter;
10 import java.net.ServerSocket;
11 import java.net.Socket;
12 
13 public class TextServer {
14     public static void main(String[] args) throws IOException, InterruptedException {
15 
16         ServerSocket ss = new ServerSocket(9999);
18         Socket s = ss.accept();
19         while(true) {
20             InputStream in = s.getInputStream();
21             byte[] b = new byte[1024];
22             int num = in.read(b);
23             String str = new String(b, 0, num);
24             if("over".equals(str)) {
25                 break;
26             }
27             Thread.sleep(1000);
28             OutputStream out = s.getOutputStream();
29             out.write(str.toUpperCase().getBytes());
30             
31         }
32         
33         ss.close();
34         s.close();
35         
36 
37     }
38 }

更多关于TCP编程的知识参考:https://www.cnblogs.com/hysum/p/7531529.html (个人认为写的很棒!)

更多内容之后再补充!关于多线程和流的知识有时间时需要加以补充!

 

相关文章: