一般情况下,服务器的长连接和短连接不是服务器说了算,而是客户端说了算。因为服务器是给别人提供业务的,一旦连接建立起来之后,服务器端不会主动把连接给close掉。

  客户端发送一笔业务,没有关闭连接,然后又发送一笔业务,还是没有关闭连接,这个连接叫长连接,就是说客户端和服务器端建立完业务以后,就不断开连接了。建立连接需要很长时间,优化服务器一般就是优化连接,

  客户端每做一次通信就连接一下服务器,也就是每做一次通信就建立一个连接,然后断掉。这叫短连接。

短连接的示例程序如下:

 #include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */ int main()
{
int i = ;
for(i = ; i < ; i++)
{
int sockfd = ;
sockfd = socket(AF_INET, SOCK_STREAM, ); struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons();
inet_aton("192.168.31.128", &addr.sin_addr);
//addr.sin_addr.s_addr = inet_addr("192.168.31.128"); if( connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == - )
{
perror("connect error");
exit();
} char recvbuf[] = {};
char sendbuf[] = {}; sprintf(sendbuf, "i : %d\n", i); write(sockfd, sendbuf, strlen(sendbuf)); read(sockfd, recvbuf, sizeof(recvbuf)); fputs(recvbuf, stdout);
memset(recvbuf, , sizeof(recvbuf));
memset(sendbuf, , sizeof(sendbuf)); close(sockfd);
} return ;
}

每发一次报文就连接一次,一共进行了10次连接,执行结果如下:

如果需要和服务器频繁的交互,长连接比较好。如果长时间不发一次报文,则短连接好。

客户端和服务器建立连接后,假如20分钟不动,特别是在公网上,则这个连接有可能被TCP IP协议重置,再发报文就是错误码。20分钟不动就重置连接这种操作可以在服务器提前设置。

p2p聊天程序,模型如下:

客户端的父进程从键盘接收数据,然后发送给服务器,服务器父进程接收数据并打印。 服务器子进程从键盘接收数据,然后发送给客户端,客户端的子进程接收数据并打印。

服务器端程序如下:

 #include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */ int main()
{
int sockfd = ;
sockfd = socket(AF_INET, SOCK_STREAM, ); if(sockfd == -)
{
perror("socket error");
exit();
} struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons();
inet_aton("192.168.31.128", &addr.sin_addr);
//addr.sin_addr.s_addr = inet_addr("192.168.6.249");
//addr.sin_addr.s_addr = INADDR_ANY; int optval = ;
if( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < )
{
perror("setsockopt error");
exit();
} if( bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < )
{
perror("bind error");
exit();
} if(listen(sockfd, SOMAXCONN) < )
{
perror("listen error");
exit();
} struct sockaddr_in peeraddr;
socklen_t peerlen; int conn = ; char *p = NULL;
int peerport = ;
p = inet_ntoa(peeraddr.sin_addr);
peerport = ntohs(peeraddr.sin_port); char recvbuf[] = {};
int ret = ; conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen);
if(conn == -)
{
perror("accept error");
exit();
} pid_t pid = ;
pid = fork(); if(pid > )
{
printf("peeraddr = %s\n peerport = %d\n", p, peerport);
char recvbuf[] = {}; while()
{
ret = read(conn, recvbuf, sizeof(recvbuf)); if(ret == )
{
printf("peer closed \n");
exit();
}
else if(ret < )
{
perror("read error");
exit();
} printf("recvive from client : %s", recvbuf);
//fputs(recvbuf, stdout); }
}
else if(pid == )
{
close(sockfd);
char sendbuf[] = {}; while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
write(conn, sendbuf, strlen(sendbuf));
memset(sendbuf, , sizeof(sendbuf));
}
close(conn);
}
else
{
perror("fork error");
close(conn);
close(sockfd);
exit();
} return ;
}

客户端程序如下:

 #include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */ int main()
{
int sockfd = ;
sockfd = socket(AF_INET, SOCK_STREAM, ); struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons();
inet_aton("192.168.31.128", &addr.sin_addr);
//addr.sin_addr.s_addr = inet_addr("192.168.31.128"); if( connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == - )
{
perror("connect error");
exit();
} int pid = fork(); if(pid > )
{
char sendbuf[] = {};
while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
write(sockfd, sendbuf, strlen(sendbuf));
memset(sendbuf, , sizeof(sendbuf));
}
close(sockfd);
}
else if(pid == )
{
char recvbuf[] = {};
while()
{
read(sockfd, recvbuf, sizeof(recvbuf));
printf("recvive from server : %s", recvbuf);
//fputs(recvbuf, stdout);
memset(recvbuf, , sizeof(recvbuf));
}
}
else
{
perror("fork error");
exit();
} return ;
}

执行结果如下:

TCP IP协议流协议,服务器读到\0时就知道客户端已经断开连接了。 什么时候客户端会发送'\0'呢? 就是当客户端关闭套接字时,TCP IP协议栈会发送一个FIN,这时候服务器端如果继续read的话,就会读到一个'\0' 。当客户端按下ctrl+c时,TCP IP协议栈就会发出FIN了,这时服务器端就能检测到客户端关闭了,如下所示:

服务器端的read读到0之后,父进程就退出了,迅速查看进程状态,发现服务器端子进程没有死掉,如下:

在父进程退出时,我们需要通知子进程也要退出,防止出现孤儿进程。

修改服务器端程序如下:

 #include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */ void handler(int num)
{
printf("receive num : %d\n", num);
exit();
} int main()
{
int sockfd = ; signal(SIGUSR1, handler); sockfd = socket(AF_INET, SOCK_STREAM, ); if(sockfd == -)
{
perror("socket error");
exit();
} struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons();
inet_aton("192.168.31.128", &addr.sin_addr);
//addr.sin_addr.s_addr = inet_addr("192.168.6.249");
//addr.sin_addr.s_addr = INADDR_ANY; int optval = ;
if( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < )
{
perror("setsockopt error");
exit();
} if( bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < )
{
perror("bind error");
exit();
} if(listen(sockfd, SOMAXCONN) < )
{
perror("listen error");
exit();
} struct sockaddr_in peeraddr;
socklen_t peerlen; int conn = ; char *p = NULL;
int peerport = ;
p = inet_ntoa(peeraddr.sin_addr);
peerport = ntohs(peeraddr.sin_port); char recvbuf[] = {};
int ret = ; conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen);
if(conn == -)
{
perror("accept error");
exit();
} pid_t pid = ;
pid = fork(); if(pid > )
{
printf("peeraddr = %s\n peerport = %d\n", p, peerport);
char recvbuf[] = {}; while()
{
ret = read(conn, recvbuf, sizeof(recvbuf)); if(ret == )
{
printf("client closed \n");
break;
//exit(0);
}
else if(ret < )
{
perror("read error");
break;
//exit(0);
} printf("recvive from client : %s", recvbuf);
//fputs(recvbuf, stdout);
} kill(pid, SIGUSR1);
wait(NULL); }
else if(pid == )
{
close(sockfd);
char sendbuf[] = {}; while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
write(conn, sendbuf, strlen(sendbuf));
memset(sendbuf, , sizeof(sendbuf));
}
}
else
{
perror("fork error");
close(conn);
close(sockfd);
exit();
} close(conn);
close(sockfd);
return ;
}

当服务端read接收到0之后,跳出循环,发送信号,并等待子进程死亡。子进程在信号处理函数中执行exit退出。

执行结果如下:

客户端程序中,子进程没有对read函数进行处理,所以,当服务器端执行主动关闭时,客户端父进程和子进程都不会退出,下面我们加入read函数的返回值处理。

程序如下:

 #include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */ void handler(int num)
{
printf("receive num : %d\n", num);
wait(NULL);
exit();
} int main()
{
int sockfd = ;
signal(SIGUSR1, handler); sockfd = socket(AF_INET, SOCK_STREAM, ); struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons();
inet_aton("192.168.31.128", &addr.sin_addr);
//addr.sin_addr.s_addr = inet_addr("192.168.31.128"); if( connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == - )
{
perror("connect error");
exit();
} int pid = fork(); if(pid > )
{
char sendbuf[] = {};
while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
write(sockfd, sendbuf, strlen(sendbuf));
memset(sendbuf, , sizeof(sendbuf));
}
close(sockfd);
}
else if(pid == )
{
char recvbuf[] = {};
int ret = ;
while()
{
ret = read(sockfd, recvbuf, sizeof(recvbuf));
if(ret == )
{
printf("server closed\n");
break;
} if(ret < )
{
printf("read error\n");
break;
} printf("recvive from server : %s", recvbuf);
//fputs(recvbuf, stdout);
memset(recvbuf, , sizeof(recvbuf));
} close(sockfd);
kill(getppid(), SIGUSR1);
exit(); }
else
{
perror("fork error");
exit();
} return ;
}

执行结果如下:

6.1 socket 长连接、短连接的更多相关文章

  1. 长连接 短连接 RST报文

    https://baike.baidu.com/item/短连接 短连接(short connnection)是相对于长连接而言的概念,指的是在数据传送过程中,只在需要发送数据时,才去建立一个连接,数 ...

  2. nginx 代理tcp长连接短连接配置

    https://blog.csdn.net/tayinyinyueyue/article/details/78932697 nginx使用ngx_stream_core_module模块代理tcp长连 ...

  3. Socket 长连接 短连接 心跳 JAVA SOCKET编程

    简单解释就是: 短连接:建立连接,发送数据包.关闭连接 长连接:建立连接.发送数据包,发送心跳包,发送数据包,发送心跳包.发送心跳包. ..... 所以又频繁的数据收发的话.短连接会频繁创建TCP连接 ...

  4. [Golang] 从零開始写Socket Server(3): 对长、短连接的处理策略(模拟心跳)

    通过前两章,我们成功是写出了一套凑合能用的Server和Client,并在二者之间实现了通过协议交流.这么一来,一个简易的socket通讯框架已经初具雏形了,那么我们接下来做的.就是想办法让这个框架更 ...

  5. Socket 接收本地短连接并转发为长连接 多线程

    import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io. ...

  6. java socket 长连接 短连接

    长连接 是一旦一个客户端登陆上服务器,其与服务器之间的连接就不关闭,不管他们之间进行了多少次交易,直到客户端退出登陆或网络出现故障.这种技术在联机交易系统实现有利于提高效率. 短连接是客户端每发一个请 ...

  7. HTTP长连接短连接

    一.什么是长连接 HTTP1.1规定了默认保持长连接(HTTP persistent connection ,也有翻译为持久连接),数据传输完成了保持TCP连接不断开(不发RST包.不四次握手),等待 ...

  8. MySQL 线程池&连接池&长连接&短连接

    线程池 简介 1.mysql每连接每线程,mysql都分配一个单独的线程,该线程处理客户端发来的所有命令 2.每个线程会占用一定的系统资源,线程数越多消耗的系统资源也越多 3.线程的创建和销毁有一定的 ...

  9. [PHP] time_wait与长连接短连接

    服务端上查看tcp连接的建立情况,直接使用netstat命令来统计,看到了很多的time_wait状态的连接.这些状态是tcp连接中主动关闭的一方会出现的状态.该服务器是nginx的webserver ...

  10. tcp & 长连接 短连接

    参考文档: tcp协议 http://blog.chinaunix.net/uid-26833883-id-3627644.html 长连接和短连接 http://blog.csdn.net/free ...

随机推荐

  1. Win7下怎么设置让远程桌面连接记住密码下次登录不需再输入

    远程桌面连接功能想必大家都不会陌生吧,特别是使用VPS服务器的用户们经常会用到,为了服务器的安全每次都会把密码设置的很复制,但是这样也有一个麻烦,就是每次要桌面远程连接的时候都要输入这么复杂的密码,很 ...

  2. django查询集API

    本节将详细介绍查询集的API,它建立在下面的模型基础上,与上一节的模型相同: from django.db import models class Blog(models.Model): name = ...

  3. JDK 12又来了,我学不动了...

    写在前面 看到 JDK 12又发布了,萌新不知不觉感觉瑟瑟发抖,从 Java 1.8的函数式编程思维和范式 到 Java 1.9的模块化特性的加持 以及还没来得及深切感受一下 Java 1.11 的 ...

  4. Spring AMQP 源码分析 08 - XML 配置

    ### 准备 ## 目标 通过 XML 配置文件使用 Spring AMQP ## 前置知识 <Spring AMQP 源码分析 07 - MessageListenerAdapter> ...

  5. .net 获取邮箱邮件列表和内容

    需求: 最近项目里遇到了个问题,对方没有提供直接获取数据源的api接口,而是通过邮件发数据的.这就需要接收邮件后解析邮件内容获得数据. 分析: 想在代码里实时获取邮件,可以通过邮件的几个协议来实现,常 ...

  6. 生物信息Python-从入门到精通?

    Python开发的方向太多了,有机器学习,数据挖掘,网络开发,爬虫等等.其实在生信领域,Python还显现不出绝对的优势,生信的大部分软件流程都是用shell或Perl写的,而且已经足够好用了.我选P ...

  7. 20170731xlVba根据数据表和模板表生成新表

    Public Sub SplitData() Dim Wb As Workbook Dim Sht As Worksheet Dim NewSht As Worksheet Dim arr As Va ...

  8. 20170724wdVBA正则表达式提取答案到Excel

    Public Sub RegExtractData() Dim StartTime, UsedTime StartTime = VBA.Timer Dim FilePath$ Dim FileName ...

  9. (转)nginx做转发时,带'_'的header内容丢失

    原本在测试环境测试通过的APP,今天准备切到线上环境做最后测试,结果发现了错误.查看日志发现是APP端发送的http请求中的header内容丢失了.那么代码没有改动,怎么平白无故会丢失头信息? 于是想 ...

  10. Jmeter响应中中文乱码解决办法

    在jmeter的bin目录下有一个jmeter.properties的文件,打开它,搜索sampleresult.default.encoding,把它的注释打开,也就是把最前面的#去掉,改成samp ...