`
sungyang
  • 浏览: 21097 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

TCP长连接、短连接

阅读更多

一、TCP连接:

当网络通信时采用TCP协议时,在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,连接的建立是需要三次握手的,而释放则需要4次握手,所以说每个连接的建立都是需要资源消耗和时间消耗的。

 

三次握手(Three-Way Handshake)即建立TCP连接,就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发,整个流程如下图所示:


 

(1)第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
(2)第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
(3)第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。
 SYN攻击:
    在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现形:#netstat -nap | grep SYN_RECV;

 

四次挥手(Four-Way Wavehand)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发,整个流程如下图所示:



 

由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此。
(1)第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
(2)第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
(3)第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
(4)第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。
上面是一方主动关闭,另一方被动关闭的情况,实际中还会出现同时发起主动关闭的情况,具体流程如下图:


二、长连接与短链接

长连接: 指在一个TCP连接上可以连续发送多个数据包,
        在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接;
        一般需要自己做在线维持。 
短连接: 指通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接;
        一般银行都使用短连接。 
        它的优点是:管理起来比较简单,存在的连接都是有用的连接,不需要额外的控制手段 
比如http的,只是连接、请求、关闭,过程时间较短,服务器若是一段时间内没有收到请求即可关闭连接。 
其实长连接是相对于通常的短连接而说的,也就是长时间保持客户端与服务端的连接状态。
长连接与短连接的操作过程 
通常的短连接操作步骤是: 
  连接→数据传输→关闭连接;
而长连接通常就是: 
  连接→数据传输→保持连接(心跳)→数据传输→保持连接(心跳)→……→关闭连接; 
这就要求长连接在没有数据通信时,定时发送数据包(心跳),以维持连接状态,
短连接在没有数据传输时直接关闭就行了
什么时候用长连接,短连接?
长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。
每个TCP连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,
所以每个操作完后都不断开,下次次处理时直接发送数据包就OK了,不用建立TCP连接。
例如:数据库的连接用长连接, 
如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。

三、发送接收方式
1、异步 
报文发送和接收是分开的,相互独立的,互不影响。这种方式又分两种情况: 
(1)异步双工:接收和发送在同一个程序中,由两个不同的子进程分别负责发送和接收 
(2)异步单工:接收和发送是用两个不同的程序来完成。 
2、同步 
报文发送和接收是同步进行,既报文发送后等待接收返回报文。 
同步方式一般需要考虑超时问题,即报文发出去后不能无限等待,需要设定超时时间,
超过该时间发送方不再等待读返回报文,直接通知超时返回。
在长连接中一般是没有条件能够判断读写什么时候结束,所以必须要加长度报文头。
读函数先是读取报文头的长度,再根据这个长度去读相应长度的报文。
四、单工、半双工和全双工
根据通信双方的分工和信号传输方向可将通信分为三种方式:
单工、
半双工、
全双工。
在计算机网络中主要采用双工方式,其中:
局域网采用半双工方式,
城域网和广域网采用全双年方式。   
1. 单工(Simplex)方式:
通信双方设备中发送器与接收器分工明确,只能在由发送器向接收器的单一固定方向上传送数据。
采用单工通信的典型发送设备如早期计算机的读卡器,典型的接收设备如打印机。   
2. 半双工(Half Duplex)方式:
通信双方设备既是发送器,也是接收器,两台设备可以相互传送数据,但某一时刻则只能向一个方向传送数据。
例如,步话机是半双工设备,因为在一个时刻只能有一方说话。   
3. 全双工(Full Duplex)方式:
通信双方设备既是发送器,也是接收器,两台设备可以同时在两个方向上传送数据。
例如,电话是全双工设备,因为双方可同时说话。
而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,
而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,
如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。
所以并发量大,但每个用户无需频繁操作情况下需用短连好。
总之,长连接和短连接的选择要视情况而定。

五、长连接demo

 

package cn.cloudBy.tcp;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;


public class KeepAlive implements Serializable{

	private static final long serialVersionUID = -2813120366138988480L;

	/* 覆盖该方法,仅用于测试使用。
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"\t维持连接包";
	}

}

 

package cn.cloudBy.tcp;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.ConcurrentHashMap;

 
public class Client {

	/**
	 * 处理服务端发回的对象,可实现该接口。
	 */
	public static interface ObjectAction{
		void doAction(Object obj,Client client);
	}
	public static final class DefaultObjectAction implements ObjectAction{
		public void doAction(Object obj,Client client) {
			System.out.println("处理:\t"+obj.toString());
		}
	}
	public static void main(String[] args) throws UnknownHostException, IOException {
		String serverIp = "127.0.0.1";
		int port = 65432;
		Client client = new Client(serverIp,port);
		client.start();
	}
	
	private String serverIp;
	private int port;
	private Socket socket;
	private boolean running=false;
	private long lastSendTime;
	private ConcurrentHashMap<Class, ObjectAction> actionMapping = new ConcurrentHashMap<Class,ObjectAction>();
	
	public Client(String serverIp, int port) {
		this.serverIp=serverIp;this.port=port;
	}
	
	public void start() throws UnknownHostException, IOException {
		if(running)return;
		socket = new Socket(serverIp,port);
		System.out.println("本地端口:"+socket.getLocalPort());
		lastSendTime=System.currentTimeMillis();
		running=true;
		new Thread(new KeepAliveWatchDog()).start();
		new Thread(new ReceiveWatchDog()).start();
	}
	
	public void stop(){
		if(running)running=false;
	}
	
	 
	public void addActionMap(Class<Object> cls,ObjectAction action){
		actionMapping.put(cls, action);
	}

	public void sendObject(Object obj) throws IOException {
		ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
		oos.writeObject(obj);
		System.out.println("发送:\t"+obj);
		oos.flush();
	}
	
	class KeepAliveWatchDog implements Runnable{
		long checkDelay = 10;
		long keepAliveDelay = 2000;
		public void run() {
			while(running){
				if(System.currentTimeMillis()-lastSendTime>keepAliveDelay){
					try {
						Client.this.sendObject(new KeepAlive());
					} catch (IOException e) {
						e.printStackTrace();
						Client.this.stop();
					}
					lastSendTime = System.currentTimeMillis();
				}else{
					try {
						Thread.sleep(checkDelay);
					} catch (InterruptedException e) {
						e.printStackTrace();
						Client.this.stop();
					}
				}
			}
		}
	}
	
	class ReceiveWatchDog implements Runnable{
		public void run() {
			while(running){
				try {
					InputStream in = socket.getInputStream();
					if(in.available()>0){
						ObjectInputStream ois = new ObjectInputStream(in);
						Object obj = ois.readObject();
						System.out.println("接收:\t"+obj);
						ObjectAction oa = actionMapping.get(obj.getClass());
						oa = oa==null?new DefaultObjectAction():oa;
						oa.doAction(obj, Client.this);
					}else{
						Thread.sleep(10);
					}
				} catch (Exception e) {
					e.printStackTrace();
					Client.this.stop();
				} 
			}
		}
	}
	
}
 
package cn.cloudBy.tcp;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ConcurrentHashMap;


public class Server {

	/**
	 * 要处理客户端发来的对象,并返回一个对象,可实现该接口。
	 */
	public interface ObjectAction {
		Object doAction(Object rev);
	}

	public static final class DefaultObjectAction implements ObjectAction {
		public Object doAction(Object rev) {
			System.out.println("处理并返回:" + rev);
			return rev;
		}
	}

	public static void main(String[] args) {
		int port = 65432;
		Server server = new Server(port);
		server.start();
	}

	private int port;
	private volatile boolean running = false;
	private long receiveTimeDelay = 3000;
	private ConcurrentHashMap<Class, ObjectAction> actionMapping = new ConcurrentHashMap<Class, ObjectAction>();
	private Thread connWatchDog;

	public Server(int port) {
		this.port = port;
	}

	public void start() {
		if (running)
			return;
		running = true;
		connWatchDog = new Thread(new ConnWatchDog());
		connWatchDog.start();
	}

	@SuppressWarnings("deprecation")
	public void stop() {
		if (running)
			running = false;
		if (connWatchDog != null)
			connWatchDog.stop();
	}

	public void addActionMap(Class<Object> cls, ObjectAction action) {
		actionMapping.put(cls, action);
	}

	class ConnWatchDog implements Runnable {
		public void run() {
			try {
				ServerSocket ss = new ServerSocket(port, 5);
				while (running) {
					Socket s = ss.accept();
					new Thread(new SocketAction(s)).start();
				}
			} catch (IOException e) {
				e.printStackTrace();
				Server.this.stop();
			}

		}
	}

	class SocketAction implements Runnable {
		Socket s;
		boolean run = true;
		long lastReceiveTime = System.currentTimeMillis();

		public SocketAction(Socket s) {
			this.s = s;
		}

		public void run() {
			while (running && run) {
				if (System.currentTimeMillis() - lastReceiveTime > receiveTimeDelay) {
					overThis();
				} else {
					try {
						InputStream in = s.getInputStream();
						if (in.available() > 0) {
							ObjectInputStream ois = new ObjectInputStream(in);
							Object obj = ois.readObject();
							lastReceiveTime = System.currentTimeMillis();
							System.out.println("接收:\t" + obj);
							ObjectAction oa = actionMapping.get(obj.getClass());
							oa = oa == null ? new DefaultObjectAction() : oa;
							Object out = oa.doAction(obj);
							if (out != null) {
								ObjectOutputStream oos = new ObjectOutputStream(
										s.getOutputStream());
								oos.writeObject(out);
								oos.flush();
							}
						} else {
							Thread.sleep(10);
						}
					} catch (Exception e) {
						e.printStackTrace();
						overThis();
					}
				}
			}
		}

		private void overThis() {
			if (run)
				run = false;
			if (s != null) {
				try {
					s.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			System.out.println("关闭:" + s.getRemoteSocketAddress());
		}

	}

}
 六、附

关于三次握手与四次挥手通常都会有典型的面试题,在此提出供有需求的XDJM们参考:
(1)三次握手是什么或者流程?四次握手呢?答案前面分析就是。
(2)为什么建立连接是三次握手,而关闭连接却是四次挥手呢?

这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。 
  • 大小: 11 KB
  • 大小: 11.6 KB
  • 大小: 9.1 KB
分享到:
评论

相关推荐

    TCP中长连接短连接

    本文将详细探讨TCP中的长连接和短连接,这两种连接方式在实际应用中的差异和选择。 长连接,也称为持久连接,指的是客户端(Client)与服务器端(Server)在完成连接建立后,保持连接状态不立即断开,以便于进行多...

    TCP长短连接简单Demo

    TCP连接分为长连接和短连接,这两种连接方式各有其特点和适用场景。本Demo是用C++语言在VS2017环境下编写的,旨在帮助开发者理解TCP长连接和短连接的实现。 首先,我们要理解TCP连接的基本概念。TCP是一种面向连接...

    TCP长连接与短连接示意图

    TCP长连接与短连接示意图

    基于Apache Mina实现的TCP长连接和短连接实例

    TCP连接分为两种类型:长连接和短连接。 1. **TCP短连接**:在短连接中,每次通信结束后,连接都会被关闭。这种方式适用于一次性、短暂的交互,如HTTP请求。然而,频繁的短连接可能会增加握手和释放连接的开销,...

    TCP/IP长连接和短连接

    在TCP/IP通信程序设计中,长连接和短连接是两种主要的连接方式,它们各自有其特点和适用场景。理解这两种连接方式对于开发者来说至关重要,因为它们直接影响到系统的效率、资源管理和安全性。 **长连接**是指客户端...

    线程、线程池、TCP协议长连接短连接的基本入门知识

    在计算机科学领域,多线程和线程池是并发编程中的关键概念,而TCP协议作为互联网通信的基础,其长连接和短连接特性则直接影响网络应用的性能和效率。本篇文章将深入浅出地介绍这些基础知识,帮助初学者理解并掌握。 ...

    【Linux网络编程笔记】TCP短连接产生大量TIME_WAIT导致无法对外建立新TCP连接的原因及解决方法—实践篇 - slv

    2. **使用长连接**:减少TCP短连接的使用,尽量维持TCP连接的持久化,以减少TIME_WAIT连接的生成。 3. **延迟关闭连接**:在应用层适当延长连接关闭的时间,让连接在完成更多任务后再进入TIME_WAIT状态。 4. **...

    Java实现Socket长连接和短连接

    - **关闭策略**:合理设置超时时间,当连接长时间无数据传输时,可以考虑关闭连接以释放资源。 **4. 应用场景** 短连接适合一次性、低延迟、资源有限的场景,如网页浏览。而长连接适合实时性强、需要持续交互的...

    TCP长连接Socket心跳收发消息

    这就是所谓的“短连接”。然而,在某些场景下,频繁地建立和断开连接会增加额外的开销,因此TCP长连接应运而生。 TCP长连接是指在完成初始化的三次握手后,客户端与服务器之间保持连接状态不立即关闭,以便在需要时...

    Mina实现长连接和短连接实例

    Apache Mina是一个流行的Java框架,专门用于简化和优化网络应用开发,它支持多种协议如TCP/IP、UDP/IP等,并提供了长连接和短连接的支持。在这个实例中,我们将探讨如何使用Mina实现长连接和短连接。 首先,理解长...

    [线上问题] “服务端长连接与客户端短连接引起Nginx产生大量\"TIME_WAIT\"状态的线程”的问题分析解决

    本文主要涉及的网络编程知识点包括长连接与短连接的定义和区别、TCP连接的建立与断开过程、以及长连接与短连接的不同使用场景和报文传输方式。 长连接和短连接是两种常见的网络通信模式,长连接也被称为持久连接,...

    TCP.rar_MFC tcp网络通信_TCP协议mfc_mfc tcp通信_tcp 短连接_tcp短连接

    1. **创建CSocket对象**:首先,你需要创建一个CSocket对象,这将作为TCP连接的基础。 2. **绑定和监听**:然后,服务器端的CSocket对象需要绑定到一个特定的IP地址和端口,并开启监听,等待客户端的连接请求。 3....

    Mina长连接短连接实例

    本文将深入探讨Mina框架中的长连接与短连接,并通过提供的Minaclient和MinaHost工程实例进行详细解析。 首先,我们需要了解什么是长连接和短连接。在TCP/IP通信中,短连接是指一次完整的通信过程(如HTTP请求)结束...

    c# Socket长连接 短链接 自己封装 通讯

    标题“c# Socket长连接 短链接 自己封装 通讯”揭示了我们将讨论的主题:如何使用C#实现Socket的长连接和短连接,并自定义通信协议。这里的关键点包括: 1. **Socket基础**:Socket是网络通信中的一个抽象概念,它...

    tcp短连接TIME_WAIT问题解决方法

    tcp连接是网络编程中最基础的概念,基于不同的使用场景,我们一般区分为“长连接”和“短连接”, 长短连接的优点和缺点这里就不详细展开了,有心的同学直接去google查询,本文主要关注如何解决tcp短连接的TIME_WAIT...

    C语言长连接服务器Demo(epoll非阻塞)

    相比短连接,长连接减少了握手和释放连接的开销,提高了系统性能,尤其是在高并发场景下。 C语言中的网络编程主要依赖于套接字(socket)API。创建一个TCP服务器的基本步骤包括:创建套接字、绑定地址、监听连接...

    socket短连接和长连接 多线程的应用

    "短连接"和"长连接"是Socket连接的两种不同模式,它们在处理网络请求时有着显著的区别。 短连接(Short Connection)通常用于一次性或者较少交互的服务,如HTTP协议就是基于短连接的。在短连接中,每次通信完成后,...

    w5500TCP server解决有时连接断开问题

    当两台设备之间建立TCP连接后,Keep-Alive功能会在一段时间无数据交换时发送一个空的数据包(称为Keep-Alive probe)到对方,以确认连接是否仍然有效。如果在一定时间内未收到响应,TCP会重新发送probe,经过多次...

    基于ASIO封装的网络库,支持TCP/UDP,支持长连接、短连接,支持组播、单播、广播

    本项目基于ASIO进行封装,提供了对TCP和UDP协议的支持,同时涵盖了长连接、短连接以及组播、单播和广播等高级网络通信模式。现在我们将深入探讨这些知识点。 首先,TCP(Transmission Control Protocol)是一种面向...

Global site tag (gtag.js) - Google Analytics