作为进程间通信及网络通信的一种重要技术,在实际的开发中,socket编程是经常被用到的。关于socket编程的一般步骤,这里不再赘述,相关资料和文章很多,google/baidu即可。

本文主要是探讨如何更好地进行socket读写处理,以及如何检测连接断开。

首先,有以下几点需要注意:

  1. 对于全双工的socket,同时读写是没问题的。比如,一个socket程序有两个线程,一个线程对socket进行读操作(recv/read),一个线程对socket进行写操作(send/write),这里是不需要进行互斥操作或做临界区保护的。
  2. 在Unix系统下, 对一个对端已经关闭的socket调用两次write,第二次将会生成SIGPIPE信号,该信号的默认处理动作是终止进程。为了防止在这种情况下导致进程退出,我们需要屏蔽该信号的默认处理动作。有两种方法,a) 在程序开头调用signal(SIGPIPE, SIG_IGN),忽略SIGPIPE信号的默认动作;b) 采用send函数的MSG_NOSIGNAL标志位,忽略SIGPIPE信号。当然,虽然我们忽略了SIGPIPE,但errno还是会被设置为EPIPE的。因此,我们可以根据这点,按照我们的情况来进行相应的处理了。
  3. 对文件描述符进行select/poll操作,a) 如果select/poll返回值大于0,此时进行recv/read,recv/read返回0,表明连接关闭; b)  recv/read返回-1,并且errno为ECONNRESET、EBADF、EPIPE、ENOTSOCK之一时,也表明连接关闭。
  4. 对文件描述符进行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:读写处理及连接断开的检测的更多相关文章

  1. socket 如何判断远端服务器的连接状态?连接断开,需重连

    fluent-logger-java is a Java library, to record events via Fluentd, from Java application. https://g ...

  2. TCP socket如何判断连接断开

    http://blog.csdn.net/zzhongcy/article/details/21992123 SO_KEEPALIVE是系统底层的机制,用于系统维护每一个tcp连接的. 心跳线程属于应 ...

  3. socket读写返回值的处理

    在调用socket读写函数read(),write()时,都会有返回值.如果没有正确处理返回值,就可能引入一些问题 总结了以下几点 1当read()或者write()函数返回值大于0时,表示实际从缓冲 ...

  4. JAVA网络编程Socket常见问题 【长连接专题】

    一. 网络程序运行过程中的常见异常及处理 第1个异常是 java.net.BindException:Address already in use: JVM_Bind. 该异常发生在服务器端进行new ...

  5. ssh连接断开后 shell进程退出

    问题描述:当SSH远程连接到服务器上,然后运行一个服务 ./catalina.sh start,然后把终端开闭(切断SSH连接)之后,发现该服务中断,导致网页无法访问.   解决方法:使用nohup命 ...

  6. zookeeper 大量连接断开重连原因排查

    转自:http://blog.csdn.net/hengyunabc/article/details/41450003?utm_source=tuicool&utm_medium=referr ...

  7. 通过socket实现多个连接并实现ssh功能

    一.前言 上一篇中我们已经知道了客户端通过socket来连接服务端,进行了一次数据传输,那如何实现客户端多次发生数据?而服务端接受多个客户端呢? 二.发送中文信息 在python3中,socket只能 ...

  8. python socket实现多个连接

    socket实现多个连接 前戏很重要~~ 在实现多个连接之前,先实现下多次发送和接收数据. 如果要多次接收数据,那么在服务器端的接收和客户端的发送部分就必须使用循环. 以下代码在python3.5下运 ...

  9. python通过socket实现多个连接并实现ssh功能详解

    python通过socket实现多个连接并实现ssh功能详解 一.前言 上一篇中我们已经知道了客户端通过socket来连接服务端,进行了一次数据传输,那如何实现客户端多次发生数据?而服务端接受多个客户 ...

随机推荐

  1. Oracle卸载之Linux下卸载oracle11g的方法

    1.使用SQL*PLUS停止数据库  如果不能通过sysdba登陆可以用nolog用户登陆后切换至sysdba [oracle@OracleTest oracle]$ sqlplus /nolog S ...

  2. compile time - run-time

    php.net Class member variables are called "properties". You may also see them referred to ...

  3. PHP之curl扩展

    curl抓取远程文件 curl_init(); curl_setopt(); curl_exec(); curl_close(); 参数: //是curl收到HTTP response中的set-co ...

  4. 【python-opencv】22-直方图

    直方图目录: 22.1 直方图的计算,绘制与分析 22.1.1 统计直方图 22.1.2 绘制直方图 22.1.3 使用掩膜(遮罩) 22.2 直方图均衡化 22.2.1 OpenCV中的直方图均衡化 ...

  5. 如何在 vue 项目里正确地引用 jquery 和 jquery-ui的插件

    copy内容的网址: https://segmentfault.com/a/1190000007020623 使用vue-cli构建的vue项目,webpack的配置文件是分散在很多地方的,而我们需要 ...

  6. Dom最常用的API

    document方法: getElementById(id) Node 返回指定结点的引用 getElementsByTagName(name) NodeList 返回文档中所有匹配的元素的集合 cr ...

  7. Andrew Ng-ML-第九&十章-神经网络

    1.神经网络模型1 图1 这是一个神经网络的模型,通常设置一个x0,作为偏执单元或者偏置(bias)神经元. 图2 这里最后一句话,说的是系数矩阵θ,神经网络模型中,如果当前在j层有s_j个单元,在j ...

  8. MVC前后台获取Action、Controller、ID名方法 以及 路由规则

    前后台获取Action.Controller.ID名方法 前台页面:ViewContext.RouteData.Values["Action"].ToString();//获取Ac ...

  9. 如何用softmax和sigmoid来做多分类和多标签分类

    首先,说下多类分类和多标签分类的区别 多标签分类:一个样本可以属于多个类别(或标签),不同类之间是有关联的,比如一个文本被被划分成“人物”和“体育人物”两个标签.很显然这两个标签不是互斥的,而是有关联 ...

  10. Locust性能测试2-先登录场景案例

    前言 有很多网站不登录的话,是无法访问到里面的页面的,这就需要先登录了 实现场景:先登录(只登录一次),然后访问页面->我的地盘页->产品页->项目页 官方案例 下面是一个简单的lo ...