分布式架构基础
网络通信技术
socket编程
非常好的学习链接:
基本流程及注意事项(建立TCP连接)
- 服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信。
- 服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。
- 服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接。
- Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket 对象能够与服务器进行通信。
- 在服务器端,accept() 方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。
连接建立后,通过使用 I/O 流在进行通信
每一个socket都有一个输出流和一个输入流
客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。
TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送
要求:编写一个socket服务器端和socket客户端
实现功能:
- socket服务器端监听于8888端口
- socket客户端连接到服务器端,发送一串当前时间的字符串:yyyy-MM-dd HH:mm:ss.ffff
- socket服务器端接收到消息后,获取当前服务器端时间,返回当前服务器端时间和接收到客户端发送过来时间串的时间间隔,返回此间隔的数值(单位:ms)
- socket客户端把返回的间隔数值显示出来,退出程序
Socket服务端实例
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Author Fiya
* @Date 2021/6/1 2:10
*/
public class Server {
public static void main(String[] args) {
try {
// 创建socket链接,端口8888
ServerSocket server = new ServerSocket(8888);
while (true) {
Socket socket = server.accept();
ServerThread thread = new ServerThread(socket);
thread.start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ServerThread extends Thread {
private Socket socket = null;
public ServerThread(Socket socket) {this.socket = socket;}
@Override
public void run() {
InputStream is = null;
is = null;
InputStreamReader isr = null;
BufferedReader br = null;
OutputStream os = null;
PrintWriter pw = null;
try {
//输入流
is = socket.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
String info = null;
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS");
long recvTime;
long nowTime;
long diff = 0;
while ((info = br.readLine()) != null) {
System.out.println("接受来自客户端的信息:"+info);
recvTime = f.parse(info).getTime();
Date now = new Date();
nowTime = f.parse(f.format(now)).getTime();
diff = nowTime - recvTime;
}
socket.shutdownInput();
os = socket.getOutputStream();
pw = new PrintWriter(os);
pw.write("响应时间"+ diff + "ms");
pw.flush();
} catch (Exception e) {
e.printStackTrace();
} finally{
//关闭资源
try {
if(pw!=null)
pw.close();
if(os!=null)
os.close();
if(br!=null)
br.close();
if(isr!=null)
isr.close();
if(is!=null)
is.close();
if(socket!=null)
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Socket客户端实例
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
/**
* @Author Fiya
* @Date 2021/6/1 4:35
*/
public class Client {
public static void main(String[] args) throws Exception {
try {
// 创建socket
Socket socket = new Socket("localhost", 8888);
// 发送消息
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
Date now = new Date();
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS");
pw.write(f.format(now));
pw.flush();
socket.shutdownOutput();
// 接收信息
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println("服务器返回:" + line);
}
br.close();
is.close();
pw.close();
os.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
TCP
TCP是面向连接的协议
三次握手
建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立, 在Socket编程中,这一过程由客户端执行connect来触发:
-
第一次握手:
Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server, Client进入SYN_SENT状态,等待Server确认。
-
第二次握手:
Server收到数据包后由标志位SYN=1知道Client请求建立连接
Server将标志位 SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求 ,Server进入SYN_RCVD状态。
-
第三次握手:
Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK 置为1,ack=K+1,并将该数据包发送给Server
Server检查ack是否为K+1,ACK是否为1,如果正确则 连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以 开始传输数据了。
四次握手
断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。 在Socket编程中,这一过程由客户端或服务端任一方执行close来触发
-
第一次挥手:
Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入 FIN_WAIT_1状态
-
第二次挥手:
Server收到FIN后发送一个ACK给Client,确认序号为收到序号+1(与SYN相同, 一个FIN占用一个序号)
Server进入CLOSE_WAIT状态。
-
第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送
Server进入LAST_ACK 状态。
-
第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1
Server进入CLOSED状态,完成四次挥手。
特点
-
面向连接
三次握手建立可靠的连接,为数据可靠传输打下基础
-
仅支持1对1传输
每条TCP传输连接只能有两个端点,不支持1对多
-
面向字节流
以字节流方式进行传输,不保留消息边界
-
高可靠性
判断丢包和误码靠的是TCP的段编号及确认号。如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据(假设丢失了)将会被重传
-
提供拥塞控制
网络拥塞时TCP能减小向网络注入数据的速率和数量,缓解拥塞
-
全双工方式传输
UDP
UDP是一种简单的、面向数据报的无连接的协议,提供的是不一定可靠的传输服务
所谓“无连接”是指在正式通信前不必与对方先建立连接,不管对方状态如何都直接发送过去。
它除了给应用程序发送数据包功能并允许它们在所需的层次上架构自己的协议之外,几乎没有做什么特别的事情。面向连接的TCP几乎做了所有的事情。
特点
-
面向无连接
发送数据想法就发,不对消息进行任何拆分和拼接操作
-
有单播,多播,广播功能
-
面向报文的
发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付IP层。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。因此,应用程序必须选择合适大小的报文
-
不可靠性
首先不可靠性体现在无连接上,通信都不需要建立连接,想发就发,这样的情况肯定不可靠。
并且收到什么数据就传递什么数据,并且也不会备份数据,发送数据也不会关心对方是否已经正确接收到数据了。
再者网络环境时好时坏,但是 UDP 因为没有拥塞控制,一直会以恒定的速度发送数据。即使网络条件不好,也不会对发送速率进行调整
日志
略