Socket:读写处理及连接断开的检测
作为进程间通信及网络通信的一种重要技术,在实际的开发中,socket编程是经常被用到的。关于socket编程的一般步骤,这里不再赘述,相关资料和文章很多,google/baidu即可。
本文主要是探讨如何更好地进行socket读写处理,以及如何检测连接断开。
首先,有以下几点需要注意:
- 对于全双工的socket,同时读写是没问题的。比如,一个socket程序有两个线程,一个线程对socket进行读操作(recv/read),一个线程对socket进行写操作(send/write),这里是不需要进行互斥操作或做临界区保护的。
- 在Unix系统下, 对一个对端已经关闭的socket调用两次write,第二次将会生成SIGPIPE信号,该信号的默认处理动作是终止进程。为了防止在这种情况下导致进程退出,我们需要屏蔽该信号的默认处理动作。有两种方法,a) 在程序开头调用signal(SIGPIPE, SIG_IGN),忽略SIGPIPE信号的默认动作;b) 采用send函数的MSG_NOSIGNAL标志位,忽略SIGPIPE信号。当然,虽然我们忽略了SIGPIPE,但errno还是会被设置为EPIPE的。因此,我们可以根据这点,按照我们的情况来进行相应的处理了。
- 对文件描述符进行select/poll操作,a) 如果select/poll返回值大于0,此时进行recv/read,recv/read返回0,表明连接关闭; b) recv/read返回-1,并且errno为ECONNRESET、EBADF、EPIPE、ENOTSOCK之一时,也表明连接关闭。
- 对文件描述符进行send/write操作,如果send/write返回-1,并且errno为ECONNRESET、EBADF、EPIPE、ENOTSOCK之一时,也表明连接关闭。
下面是一个demo程序,server端代码(server.c)为:
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <poll.h>
#include <pthread.h>
#include <errno.h> #define UNIX_PATH_MAX 108
#define SOCK_PATH "/tmp/test.sock" int sock_write(int fd, char* buf, int len)
{
int err_num, res;
char err_str[];
int disconnect = ; while(!disconnect){
/* Use send with a MSG_NOSIGNAL to ignore a SIGPIPE signal which
* would cause the process exit. */
res = send(fd, buf, len, MSG_NOSIGNAL | MSG_DONTWAIT);
if( - == res ){
err_num = errno;
printf("send error:%s!\n", strerror_r(err_num, err_str, sizeof(err_str))); switch(err_num){
case ECONNRESET:
case EBADF:
case EPIPE:
case ENOTSOCK:
disconnect = ;
break;
//case EWOULDBLOCK:
case EAGAIN:
usleep();
break;
case EINTR:
break;
default:
break;
}
}else if( res > ){
if( res < len ){
/* Incomplete information. */
buf += res;
len -= res;
}else if( res == len ){
/* Message has sended successfully. */
break;
}
}
}
return disconnect;
} void* write_handle(void* fd)
{
int len, connection_fd;
char buffer[];
char* end_flag = "\r\n"; connection_fd = *(int*)fd;
len = snprintf(buffer, , "buffer data ends with a end flag%s", end_flag);
buffer[len] = ; while( == sock_write(connection_fd, buffer, len)){
sleep();
}
} int main(void)
{
struct sockaddr_un address;
int socket_fd, connection_fd;
socklen_t address_length; socket_fd = socket(PF_UNIX, SOCK_STREAM, );
if(socket_fd < ) {
printf("%s:socket() failed\n", SOCK_PATH);
return -;
}
unlink(SOCK_PATH);
memset(&address, , sizeof(struct sockaddr_un));
address.sun_family = AF_UNIX;
snprintf(address.sun_path, UNIX_PATH_MAX, SOCK_PATH);
if(bind(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != )
{
printf("%s:bind() failed\n", SOCK_PATH);
return -;
}
if(listen(socket_fd, ) != )
{
printf("%s:listen() failed\n", SOCK_PATH);
return -;
}
while((connection_fd = accept(socket_fd, (struct sockaddr *) &address,&address_length)) > -)
{
pthread_t w_thread;
struct pollfd pfd;
char buffer[];
int nbytes;
int cnt;
int res;
int err_num;
int disconnect=;
#ifdef DEBUG
char str[];
#endif printf("\n\n%s:accept a connection!\n", SOCK_PATH); pthread_create(&w_thread, NULL, write_handle, &connection_fd); pfd.fd = connection_fd;
pfd.events = POLLIN | POLLHUP | POLLRDNORM;
pfd.revents = ; while(){
res = poll(&pfd, , );
if(res >= ){
// if result > 0, this means that there is either data available on the
// socket, or the socket has been closed
cnt = recv(connection_fd, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT);
if( == cnt ){
if(res > ){
// if recv returns zero, that means the connection has been closed.
#ifdef DEBUG
printf("connection disconnect 1!\n");
#endif
disconnect = ;
break;
}
}else if( - == cnt ){
err_num = errno;
#ifdef DEBUG
printf("recv error:%s!\n", strerror_r(errno, str, sizeof(str)));
#endif
switch(err_num){
case ECONNRESET:
case EBADF:
case EPIPE:
case ENOTSOCK:
disconnect = ;
break;
default:
break;
}
if( disconnect ){
#ifdef DEBUG
printf("connection disconnect 2!\n");
#endif
break;
}
}else if( cnt > ){
/* discard everything received from client.*/
while((nbytes = recv(connection_fd, buffer, sizeof(buffer)-, MSG_DONTWAIT)) > ){
buffer[nbytes] = ;
#ifdef DEBUG
printf("buffer:%s\n", buffer);
#endif
}
#ifdef DEBUG
if( == nbytes ){
printf("All received!\n");
}
#endif
}
}
#ifdef DEBUG
else if(res == -){
/* This case shouldn't happen, we sleep 5 seconds here in case and retry it. */
printf("Error: poll return -1!!!\n");
sleep();
}
#endif
}
close(connection_fd);
pthread_cancel(w_thread);
} close(socket_fd);
unlink(SOCK_PATH);
return ;
}
client端代码(client.c)为:
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <poll.h>
#include <errno.h> #define UNIX_PATH_MAX 108
#define SOCK_PATH "/tmp/test.sock" int main(void)
{
struct sockaddr_un address;
int socket_fd, res;
char buffer[];
pthread_t w_thread;
struct pollfd pfd;
int err_num;
int disconnect; while(){
socket_fd = socket(PF_UNIX, SOCK_STREAM, );
if(socket_fd < )
{
printf("%s:socket() failed\n", SOCK_PATH);
sleep();
continue;
}
memset(&address, , sizeof(struct sockaddr_un));
address.sun_family = AF_UNIX;
snprintf(address.sun_path, UNIX_PATH_MAX, SOCK_PATH);
if(connect(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != )
{
printf("%s:connect() failed\n", SOCK_PATH);
close(socket_fd);
socket_fd = -;
sleep();
continue;
}
#ifdef DEBUG
printf("connect success!\n");
#endif
#if 1
pfd.fd = socket_fd;
pfd.events = POLLIN | POLLHUP | POLLRDNORM;
pfd.revents = ;
disconnect = ;
while(){
// use the poll system call to be notified about socket status changes
res = poll(&pfd, , );
if(res >= ){
// if result > 0, this means that there is either data available on the
// socket, or the socket has been closed
char buffer[];
int cnt, nbytes;
cnt = recv(socket_fd, buffer, sizeof(buffer)-, MSG_PEEK | MSG_DONTWAIT);
if( - == cnt){
err_num = errno;
switch(err_num){
case ECONNRESET:
case EBADF:
case EPIPE:
case ENOTSOCK:
disconnect = ;
break;
default:
break;
}
if(disconnect){
break;
}
}
else if( == cnt){
if(res > ){
// if recv returns zero, that means the connection has been closed:
disconnect = ;
break;
}
}
else if( cnt > ){
while((nbytes = recv(socket_fd, buffer, sizeof(buffer)-, MSG_DONTWAIT)) > ){
buffer[nbytes] = ;
printf("buffer:%s\n", buffer);
}
}
}
}
#endif
close(socket_fd);
socket_fd = -;
#ifdef DEBUG
printf("server disconnect!\n");
#endif
/* here sleep 5 seconds and re-connect. */
sleep();
} return ;
}
Socket:读写处理及连接断开的检测的更多相关文章
- socket 如何判断远端服务器的连接状态?连接断开,需重连
fluent-logger-java is a Java library, to record events via Fluentd, from Java application. https://g ...
- TCP socket如何判断连接断开
http://blog.csdn.net/zzhongcy/article/details/21992123 SO_KEEPALIVE是系统底层的机制,用于系统维护每一个tcp连接的. 心跳线程属于应 ...
- socket读写返回值的处理
在调用socket读写函数read(),write()时,都会有返回值.如果没有正确处理返回值,就可能引入一些问题 总结了以下几点 1当read()或者write()函数返回值大于0时,表示实际从缓冲 ...
- JAVA网络编程Socket常见问题 【长连接专题】
一. 网络程序运行过程中的常见异常及处理 第1个异常是 java.net.BindException:Address already in use: JVM_Bind. 该异常发生在服务器端进行new ...
- ssh连接断开后 shell进程退出
问题描述:当SSH远程连接到服务器上,然后运行一个服务 ./catalina.sh start,然后把终端开闭(切断SSH连接)之后,发现该服务中断,导致网页无法访问. 解决方法:使用nohup命 ...
- zookeeper 大量连接断开重连原因排查
转自:http://blog.csdn.net/hengyunabc/article/details/41450003?utm_source=tuicool&utm_medium=referr ...
- 通过socket实现多个连接并实现ssh功能
一.前言 上一篇中我们已经知道了客户端通过socket来连接服务端,进行了一次数据传输,那如何实现客户端多次发生数据?而服务端接受多个客户端呢? 二.发送中文信息 在python3中,socket只能 ...
- python socket实现多个连接
socket实现多个连接 前戏很重要~~ 在实现多个连接之前,先实现下多次发送和接收数据. 如果要多次接收数据,那么在服务器端的接收和客户端的发送部分就必须使用循环. 以下代码在python3.5下运 ...
- python通过socket实现多个连接并实现ssh功能详解
python通过socket实现多个连接并实现ssh功能详解 一.前言 上一篇中我们已经知道了客户端通过socket来连接服务端,进行了一次数据传输,那如何实现客户端多次发生数据?而服务端接受多个客户 ...
随机推荐
- 英特尔和 Google 的 OKR 制度与我们一般所说的 KPI 有什么不同?
英特尔和 Google 的 OKR 制度与我们一般所说的 KPI 有什么不同? - 知乎 https://www.zhihu.com/question/22478049?sort=created 知乎 ...
- supervisor control in centos 6/7 python2.6.2.7 3.4
sudo yum install epel-releasesudo yum install python34 sudo pip install virtualenv yum -y install ep ...
- AOP 详解
1. 需求:统计方法执行的性能情况(来源:<精通Spring 4.x>) // 性能监视类 PerformanceMonitor package com.noodles.proxy; pu ...
- Pots--poj(bfs,输出路径)
http://poj.org/problem?id=3414 题意: 给你两个容量为a,b的杯子:有3个操作: 1:FILL(i):把第i个杯子从水库中装满: 2:DROP(i):把第i个杯子清空: ...
- Git使用常见问题脚本
receive.denyCurrentBranch 这是由于git默认拒绝了push操作,需要进行设置,修改.git/config添加如下代码: [receive] denyCurrentBr ...
- javaScript高级教程(八)-----正则表达式温故知新
1.RegExp对象:五个属性二个方法 五个属性:global, ignoreCase,multiline,lastIndex,source 二个方法: exec()--模式匹配 test()--检测 ...
- 1分钟了解协同过滤,pm都懂了
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/z50L2O08e2u4afToR9A/article/details/79565720 projec ...
- (c++) int 转 string,char*,const char*和string的相互转换
一.int 和string的相互转换 1 int 转化为 string c++ //char *itoa( int value, char *string,int radix); // 原型说明: / ...
- HTML5-CSS3-JavaScript(3)
我们就从HTML5的基础总结起.希望可以提高自身的基础. HTML5 头部 和 元信息 使用 <head.../> 元素可以定义HTML文档头,该元素可以包含如下子元素. <scri ...
- ajax请求session失效重定向到登录页面
在ajax请求的页面引入一个自定义的AjaxRedirect.js的文件 AjaxRedirect.js的代码如下: $(function(){ $.ajaxSetup({ type: 'POST', ...