6.1 socket 长连接、短连接
一般情况下,服务器的长连接和短连接不是服务器说了算,而是客户端说了算。因为服务器是给别人提供业务的,一旦连接建立起来之后,服务器端不会主动把连接给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 长连接、短连接的更多相关文章
- 长连接 短连接 RST报文
https://baike.baidu.com/item/短连接 短连接(short connnection)是相对于长连接而言的概念,指的是在数据传送过程中,只在需要发送数据时,才去建立一个连接,数 ...
- nginx 代理tcp长连接短连接配置
https://blog.csdn.net/tayinyinyueyue/article/details/78932697 nginx使用ngx_stream_core_module模块代理tcp长连 ...
- Socket 长连接 短连接 心跳 JAVA SOCKET编程
简单解释就是: 短连接:建立连接,发送数据包.关闭连接 长连接:建立连接.发送数据包,发送心跳包,发送数据包,发送心跳包.发送心跳包. ..... 所以又频繁的数据收发的话.短连接会频繁创建TCP连接 ...
- [Golang] 从零開始写Socket Server(3): 对长、短连接的处理策略(模拟心跳)
通过前两章,我们成功是写出了一套凑合能用的Server和Client,并在二者之间实现了通过协议交流.这么一来,一个简易的socket通讯框架已经初具雏形了,那么我们接下来做的.就是想办法让这个框架更 ...
- Socket 接收本地短连接并转发为长连接 多线程
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io. ...
- java socket 长连接 短连接
长连接 是一旦一个客户端登陆上服务器,其与服务器之间的连接就不关闭,不管他们之间进行了多少次交易,直到客户端退出登陆或网络出现故障.这种技术在联机交易系统实现有利于提高效率. 短连接是客户端每发一个请 ...
- HTTP长连接短连接
一.什么是长连接 HTTP1.1规定了默认保持长连接(HTTP persistent connection ,也有翻译为持久连接),数据传输完成了保持TCP连接不断开(不发RST包.不四次握手),等待 ...
- MySQL 线程池&连接池&长连接&短连接
线程池 简介 1.mysql每连接每线程,mysql都分配一个单独的线程,该线程处理客户端发来的所有命令 2.每个线程会占用一定的系统资源,线程数越多消耗的系统资源也越多 3.线程的创建和销毁有一定的 ...
- [PHP] time_wait与长连接短连接
服务端上查看tcp连接的建立情况,直接使用netstat命令来统计,看到了很多的time_wait状态的连接.这些状态是tcp连接中主动关闭的一方会出现的状态.该服务器是nginx的webserver ...
- tcp & 长连接 短连接
参考文档: tcp协议 http://blog.chinaunix.net/uid-26833883-id-3627644.html 长连接和短连接 http://blog.csdn.net/free ...
随机推荐
- poj 1986 Distance Queries 带权lca 模版题
Distance Queries Description Farmer John's cows refused to run in his marathon since he chose a pa ...
- Error: Checksum mismatch.
bogon:bin macname$ brew install go ==> Downloading https://homebrew.bintray.com/bottles-portable- ...
- SQLServer中获取所有数据库名、所有表名、所有字段名的SQL语句
----1. 获取所有的数据库名----- SELECT NAME FROM MASTER.DBO.SYSDATABASES ORDER BY NAME -----2. 获取所有的表名------ S ...
- js获取url 参数
window.getRequest = function (url) { var theRequest = new Object(); var indexOf = url.indexOf(" ...
- php 四种基础排序
1. 冒泡排序算法 * 思路分析:法如其名,就是像冒泡一样,每次从数组当中 冒一个最大的数出来. * 比如:2,4,1 // 第一次 冒出的泡是4 * ...
- CentOS配置iptables规则并使其永久生效
1. 目的 最近为了使用nginx,配置远程连接的使用需要使用iptable是设置允许外部访问80端口,但是设置完成后重启总是失效.因此百度了一下如何设置永久生效,并记录. 2. 设置 2.1 添加 ...
- Silverlight自定义控件系列 – TreeView (1)
原文路径:http://blog.csdn.net/wlanye/article/details/7265457 很多人都对MS自带的控件不太满意(虽然MS走的是简约风格),都会试图去修改或创建让 ...
- C#,ArcGIS Engine开发入门教程
C#,ArcGIS Engine开发入门教程 转自:http://blog.csdn.net/yanleigis/article/details/2233674 目录(?)[+] 五实现 一 加载A ...
- (转)理解TIME_WAIT,彻底弄清解决TCP: time wait bucket table overflow
转载自http://blog.51cto.com/benpaozhe/1767612: 一直对这个问题知其然而不知其所以然,这些日子再次碰到,看了很多的资料,彻底解决一下,呵呵,先上个图,所有理解围绕 ...
- Spring Boot 文档
本节对 Spring Boot 的参考文档做了一个简单概述.本章节对全文的参考手册进行内容上的一些索引. 你可以参考本节,从头到尾依次阅读该文档,也可以跳过不感兴趣的内容. Spring Boot 参 ...