Socket:读写处理及连接断开的检测 http://hongdow.com/page/2
来源:互联网 发布:阿里云网址是多少 编辑:程序博客网 时间:2024/06/09 14:39
作为进程间通信及网络通信的一种重要技术,在实际的开发中,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[128];
int disconnect = 0;
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( -1 == 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 = 1;
break;
//case EWOULDBLOCK:
case EAGAIN:
usleep(10000);
break;
case EINTR:
break;
default:
break;
}
}else if( res > 0 ){
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[256];
char* end_flag = "\r\n";
connection_fd = *(int*)fd;
len = snprintf(buffer, 256, "buffer data ends with a end flag%s", end_flag);
buffer[len] = 0;
while(0 == sock_write(connection_fd, buffer, len)){
sleep(1);
}
}
int main(void)
{
struct sockaddr_un address;
int socket_fd, connection_fd;
socklen_t address_length;
socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if(socket_fd < 0) {
printf("%s:socket() failed\n", SOCK_PATH);
return -1;
}
unlink(SOCK_PATH);
memset(&address, 0, 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)) != 0)
{
printf("%s:bind() failed\n", SOCK_PATH);
return -1;
}
if(listen(socket_fd, 0) != 0)
{
printf("%s:listen() failed\n", SOCK_PATH);
return -1;
}
while((connection_fd = accept(socket_fd, (struct sockaddr *) &address,&address_length)) > -1)
{
pthread_t w_thread;
struct pollfd pfd;
char buffer[512];
int nbytes;
int cnt;
int res;
int err_num;
int disconnect=0;
#ifdef DEBUG
char str[128];
#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 = 0;
while(1){
res = poll(&pfd, 1, 500);
if(res >= 0){
// 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( 0 == cnt ){
if(res > 0){
// if recv returns zero, that means the connection has been closed.
#ifdef DEBUG
printf("connection disconnect 1!\n");
#endif
disconnect = 1;
break;
}
}else if( -1 == 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 = 1;
break;
default:
break;
}
if( disconnect ){
#ifdef DEBUG
printf("connection disconnect 2!\n");
#endif
break;
}
}else if( cnt > 0 ){
/* discard everything received from client.*/
while((nbytes = recv(connection_fd, buffer, sizeof(buffer)-1, MSG_DONTWAIT)) > 0){
buffer[nbytes] = 0;
#ifdef DEBUG
printf("buffer:%s\n", buffer);
#endif
}
#ifdef DEBUG
if( 0 == nbytes ){
printf("All received!\n");
}
#endif
}
}
#ifdef DEBUG
else if(res == -1){
/* This case shouldn't happen, we sleep 5 seconds here in case and retry it. */
printf("Error: poll return -1!!!\n");
sleep(5);
}
#endif
}
close(connection_fd);
pthread_cancel(w_thread);
}
close(socket_fd);
unlink(SOCK_PATH);
return 0;
}
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[256];
pthread_t w_thread;
struct pollfd pfd;
int err_num;
int disconnect;
while(1){
socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if(socket_fd < 0)
{
printf("%s:socket() failed\n", SOCK_PATH);
sleep(5);
continue;
}
memset(&address, 0, 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)) != 0)
{
printf("%s:connect() failed\n", SOCK_PATH);
close(socket_fd);
socket_fd = -1;
sleep(5);
continue;
}
#ifdef DEBUG
printf("connect success!\n");
#endif
#if 1
pfd.fd = socket_fd;
pfd.events = POLLIN | POLLHUP | POLLRDNORM;
pfd.revents = 0;
disconnect = 0;
while(1){
// use the poll system call to be notified about socket status changes
res = poll(&pfd, 1, 60000);
if(res >= 0){
// if result > 0, this means that there is either data available on the
// socket, or the socket has been closed
char buffer[512];
int cnt, nbytes;
cnt = recv(socket_fd, buffer, sizeof(buffer)-1, MSG_PEEK | MSG_DONTWAIT);
if( -1 == cnt){
err_num = errno;
switch(err_num){
case ECONNRESET:
case EBADF:
case EPIPE:
case ENOTSOCK:
disconnect = 1;
break;
default:
break;
}
if(disconnect){
break;
}
}
else if( 0 == cnt){
if(res > 0){
// if recv returns zero, that means the connection has been closed:
disconnect = 1;
break;
}
}
else if( cnt > 0 ){
while((nbytes = recv(socket_fd, buffer, sizeof(buffer)-1, MSG_DONTWAIT)) > 0){
buffer[nbytes] = 0;
printf("buffer:%s\n", buffer);
}
}
}
}
#endif
close(socket_fd);
socket_fd = -1;
#ifdef DEBUG
printf("server disconnect!\n");
#endif
/* here sleep 5 seconds and re-connect. */
sleep(5);
}
return 0;
}
- Socket:读写处理及连接断开的检测 http://hongdow.com/page/2
- Socket 处理客户端、服务器端 断开连接
- Vxworks下检测关于socket连接关闭的问题-----拔掉网线或超时,自动断开
- 优雅地关闭Socket;检测非正常断开的TCP连接
- 关于socket编程中服务器端如何检测客户端连接断开的问题
- 优雅地关闭Socket;检测非正常断开的TCP连接
- Socket 编程经验谈---如何处理socket连接后服务器端或客户端的断开
- Socket 编程经验谈---如何处理socket连接后服务器端或客户端的断开
- Socket 编程经验谈---如何处理socket连接后服务器端或客户端的断开
- 检测SOCKET是否断开
- socket中使用心跳来检测连接是否断开
- TCP连接异常断开的检测
- TCP连接异常断开的检测
- 检测非正常断开的TCP连接
- Linux socket断开连接的怪异
- socket断开连接
- 判断Socket断开连接
- 检测socket链接是否断开
- 带复制构造函数、赋值运算符的模板队列
- 全排列算法:不含重复元素,包含重复元素,字母序排列
- 新生排位赛第四场 dp
- xml(3)--dom4j实现crud操作
- HDU1325并查集判断是否为一棵树
- Socket:读写处理及连接断开的检测 http://hongdow.com/page/2
- 身份证号的检测
- C链表之创建简单静态链表
- Http协议参数传递中编码问题(Get/Post 方式)
- Android双击以及多击事件
- 3-09. 队列中的元素排序(20)
- 2014 Multi-University Training Contest 1--by FZU 解题报告
- 电流源电路
- 兼容性好的CSS字体投影