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 ...
随机推荐
- Ubuntu 下载
http://releases.ubuntu.com/
- Docker监控怎么做?
http://dockone.io/article/1643 监控的价值与体系在运维体系中, 监控是非常重要的组成部分.通过监控可以实时掌握系统运行的状态,对故障的提前预警,历史状态的回放等,还可以通 ...
- ASP.NET MVC实现网站验证码功能
网站添加验证码,主要为防止机器人程序批量注册,或对特定的注册用户用特定程序暴力破解方式,以进行不断的登录.灌水等危害网站的操作.验证码被广泛应用在注册.登录.留言等提交信息到服务器端处理的页面中. 在 ...
- [html]webpack网页开发打包工具
官方文档:https://webpack.js.org/concepts/ 中文文档:https://doc.webpack-china.org/guides/installation/ 简书简介:h ...
- golang基础类型
1.变量 Go使用关键字var定义变量,类型信息放在变量名后面,初始化为零,如下: 在函数内部还有一种更简洁的:=定义方式 变量赋值,两个变量可直接交换 注意:函数内部定义的变量没使用的话编译器会报错 ...
- meta 标签
<meta http-equiv="Refresh" content="5;url=http://www.w3school.com.cn" /> & ...
- Unity3D中人物模型的构成
1.动画: 2.骨骼: 就是一些 Transform 组件,没有其他组件,它们会根据动画的要求而进行运动. 3.皮肤: 其上的 SkinnedMeshRenderer 关联了 网格.骨骼.材质 三个组 ...
- 转发一篇分析LinQ是什么?
LINQ(发音:Link)是语言级集成查询(Language INtegrated Query) ?LINQ是一种用来进行数据访问的编程模型,使得.NET语言可以直接支持数据查询 ?LINQ的目标是降 ...
- [.NET开发] C#编程调用Cards.dll实现图形化发牌功能示例
本文实例讲述了C#编程调用Cards.dll实现图形化发牌功能.分享给大家供大家参考,具体如下: using System; using System.Collections.Generic; usi ...
- JS-Object (3) JSON; Event Object相关知识(事件冒泡,事件监听, stopPropagation()
通常用于在网站上表示和传输数据 使用JavaScript处理JSON的所有工作,包括访问JSON对象中的数据项并编写自己的JSON. JSON text基本上就像是一个JavaScript对象,这句话 ...