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来连接服务端,进行了一次数据传输,那如何实现客户端多次发生数据?而服务端接受多个客户 ...
随机推荐
- php:// — 访问各个输入/输出流(I/O streams)
PHP: php:// - Manual http://www.php.net/manual/zh/wrappers.php.php php:// php:// — 访问各个输入/输出流(I/O st ...
- https-->http and http-->https bitransfer
openssl s_client -connect myupload.mysite.net:443/cgi-bin/posupload.cgi -status -cert client.pem -ve ...
- 安装JIRA
参考链接:https://www.cnblogs.com/houchaoying/p/9096118.html mysql-connector插件下载: https://mvnrepository.c ...
- MySQL(4):主从复制原理
1.主从复制概述 MySQL主从复制也可以称为MySQL主从同步,它是构建数据库高可用集群架构的基础.它通过将一台主机的数据复制到其他一台或多台主机上,并重新应用relay log中的SQL语句来实现 ...
- servlet输出请求头
1.参考 Enumeration headerNames = req.getHeaderNames(); while(headerNames.hasMoreElements()) { String h ...
- python 基础 特殊符号的使用
python语句中的一些基本规则和特殊符号: 1.井号# 表示之后的字符为python注释 Python注释语句从#号字符开始,注释可以在语句的任何一个地方开始,解释器会忽略掉该行#号之后的所有内容 ...
- 006-springboot2.0.4 配置log4j2,以及打印mybatis的sql
一.pom配置 普通项目 <!-- log4j2 --> <dependency> <groupId>org.apache.logging.log4j</gr ...
- Spark ClassNotFoundException $$anonfun$2
Spark ClassNotFoundException $$anonfun$2 1. 软件环境: 软件 版本 Spark 原生1.6.0 Hadoop 原生2.6.5 2. 应用场景&问题描 ...
- Tomcat清空缓存方法
把Tomcat的work目录下有个Catalina全部删除
- PAT 1103 Integer Factorization[难]
1103 Integer Factorization(30 分) The K−P factorization of a positive integer N is to write N as the ...