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来连接服务端,进行了一次数据传输,那如何实现客户端多次发生数据?而服务端接受多个客户 ...
随机推荐
- CAP 一致性协议及应用解析
https://mp.weixin.qq.com/s/26x8O1bRzurl84e3nM6TTA CAP 一致性协议及应用解析 原创: 有赞技术 有赞coder 1周前 文 | 云开 on 用户中心 ...
- 数据结构——栈(C语言实现)
#include <stdio.h> #include <stdlib.h> #include<string.h> #include<malloc.h> ...
- iOS开发tableView去掉顶部上部空表区域
tableview中的第一个cell 里上部 有空白区域,大概64像素 在viewDidLoad中加入如下代码 self.automaticallyAdjustsScrollViewInsets = ...
- Flip Game---poj1753(状压+bfs)
题目链接:http://poj.org/problem?id=1753 题意:是有一个4X4的图,b代表黑色,w代表白色,问最少翻转几次可以把所有的点变成白色或者黑色,每次翻转一个点时,可以把它 ...
- maven报错集
1.install报错解决[致命错误: 在类路径或引导类路径中找不到程序包 java.lang] Windows分隔符英文分号 <bootclasspath>${java.home}/li ...
- nodejs(五)同步异步--USING SETTIMEOUT INSTEAD OF SETINTERVAL TO FORCE SERIALIZATION
Let’s say you want a function that does some I/O — such as parsing a log fi le — that will periodica ...
- mysql 权限管理 revoke 回收权限 命令
回收所有库,所有表的所有权限 revoke all on *.* from 针对mike的账号 回收 所有库,所有表的select权限 mysql> revoke select on *.* f ...
- Hadoop自学笔记(三)MapReduce简单介绍
1. MapReduce Architecture MapReduce是一套可编程的框架,大部分MapReduce的工作都能够用Pig或者Hive完毕.可是还是要了解MapReduce本身是怎样工作的 ...
- Java-idea-生成for循环
itar 生成array for代码块 for (int i = 0; i < array.length; i++) { = array[i]; } itco 生成Collection迭代 fo ...
- gradle build scan
1:gradle build scan 用于视图审查 构建步骤如下 https://guides.gradle.org/creating-build-scans/?_ga=2.80362963.59 ...