socket编程,算是第一次接触吧(⁎⁍̴̛ᴗ⁍̴̛⁎)
理解的有点不是很深刻,所以在此记录一下(>^ω^<)
首先,我们先了解一下网络中的进程之间如何进行通信????
网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。
使用TCP/IP协议的应用程序通常采用应用编程接口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报文格式
TCP是什么?Transmission Control Protocol 传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。
TCP报文格式
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 }
解析:
-
DatagramSocket代表UDP协议的Socket,它的唯一作用就是接收和发送数据报。
-
DatagramSocket的构造器。
DatagramSocket():创建一个DatagramSocket实例,并将该对象绑定到本机默认IP地址、本机所有可用端口中随机选择的某个端口。
DatagramSocket(int prot):创建一个DatagramSocket实例,并将该对象绑定到本机默认IP地址、指定端口。
DatagramSocket(int port, InetAddress laddr):创建一个DatagramSocket实例,并将该对象绑定到指定IP地址、指定端口。
-
receive(DatagramPacket p):从该DatagramSocket中接收数据报。
send(DatagramPacket p):以该DatagramSocket对象向外发送数据报。
-
使用DatagramSocket发送数据报时,DatagramSocket并不知道将该数据报发送到哪里,而是由DatagramPacket自身决定数据报的目的地。就像码头并不知道每个集装箱的目的地,码头只是将这些集装箱发送出去,而集装箱本身包含了该集装箱的目的地。
-
DatagramPacket代表数据报。
-
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发送端编程步骤:
- 创建发送端的服务 使用DatagramSocket
- 创建一个数据包 使用DatagramPacket 将数据以字节数组的形式放入 并指定IP地址和端口号
- 发送数据包 send
- 关闭服务
UDP接收端编程步骤:
- 创建接收端的服务 使用DatagramSocket
- 准备一个空数据包 使用DatagramPacket 封装字节数组 用来接收数据
- 接收数据 receive
- 拆包获得数据 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 }
解析
- ServerSocket(int port)——创建并绑定到特定端口的服务器套接字
- accept()——侦听并接受到此套接字的连接
- close()——关闭此套接字
- getInetAddress()——得到ServerSocket对象绑定的IP地址。
- getLocalPort()——返回此套接字在其上侦听的端口
- Socket(InetAddress address, int port)——创建一个套接字并将其连接到指定ip地址的指定端口号
- Socket(String host, int port)——创建一个套接字并将其连接到指定主机上的指定端口号
- close()——关闭此套接字
- getInetAddress()——返回套接字连接的地址
- getInputStream()——返回此套接字的输入流
- 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 (个人认为写的很棒!)
更多内容之后再补充!关于多线程和流的知识有时间时需要加以补充!