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来连接服务端,进行了一次数据传输,那如何实现客户端多次发生数据?而服务端接受多个客户 ...
随机推荐
- Effective Java 目录
<Effective Java>目录摘抄. 我知道这看起来很糟糕.当下,自己缺少实际操作,只能暂时摘抄下目录.随着,实践的增多,慢慢填充更多的示例. Chapter 2 Creating ...
- ext3日志模式
ext3日志模式 http://blog.sina.com.cn/s/blog_5d4ab4b40100dosx.html ext3支持多种日志模式 ext3 是ext2文件系统的高一级版本,完全兼容 ...
- java8,方法引用
1:方法引用,https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html 当我们使用lambda表达式去创建一个 ...
- spring boot 自定义过滤器链
spring boot 会按照order值的大小,从大到小的顺序来依次过滤. 贴下代码: package com.osp.ucenter; import org.springframework.boo ...
- weka源代码-总述
分类: 所有的分类器都继承自抽象类AbstractClassifier而AbstractClassifier继承自接口Classifier.集成关系如下图所示: 而类Classifier中主要包含以下 ...
- 在Win7系统下, 使用VS2015 打开带有日文注释程序出现乱码的解决方案
在Win7系统下, 使用VS2015 打开带有日文注释程序出现乱码的解决方案 下载: apploc.msi (下载地址:http://microsoft-applocale.software.info ...
- spring-data-mongodb关于id的坑
有如下Mongo记录: 在Java中对应2个类来表示此结构: public class SG_IMAGERELATION { @Id private String id; private int gi ...
- jmeter 读取excel数据
jmeter 读取excel数据使用的方法是使用Jmeter CSV Data Set Config参数化 但是将excel文件保存成csv格式后,jmeter读取后返回的数据总是出现乱码问题, 以下 ...
- selenium webdriver模拟鼠标键盘操作
在测试使用Selenium webdriver测试WEB系统的时候,用到了模拟鼠标.键盘的一些输入操作. 1.鼠标的左键点击.双击.拖拽.右键点击等: 2.键盘的回车.回退.空格.ctrl.alt.s ...
- .net 调用API并解析Json数据方法
using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using Syst ...