您的位置:首页 > 理论基础 > 计算机网络

关于TCP套接字编程中的send()引起的断开的通道

2013-10-29 19:23 218 查看
在用多线程编写可以支持多个客户端连接的远程控制系统时,当非正常关闭客户端时服务器老出现断开的通道;

原来的代码,

#include<stdlib.h>
#include<stdio.h>
#include<sys/socket.h>
#include<netdb.h>
#include<string.h>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>

#define PORT 8900
#define SIZE 2048

FILE *mypopen(char *command,char type)
{
int pipefd[2];
pid_t pid;
char *argv[10];
int i;

if(pipe(pipefd)<0)
{
printf("create mypopen fail");
return NULL;
}

pid = fork();

if(pid==0)
{
if(type=='r')
{
close(pipefd[0]);
dup2(pipefd[1],STDOUT_FILENO);
close(pipefd[1]);
}
execl("/bin/sh","sh","-c",command,(char *) 0);
_exit(0);

}
else if(pid>0)
{
wait(0);
if(type=='r')
{
close(pipefd[1]);
return fdopen(pipefd[0],"r");
}
}

}

void 	exec (char* command,char* result)
{

FILE * in;
int len;
char c;

len=0;

bzero(result,sizeof(result));
in=mypopen(command,'r');

if (NULL==in)
{
fprintf(stderr,"error in create a pipe\n");
}

while(((c=fgetc(in))!=EOF)&&(len<SIZE))
{
result[len]=c;
len++;
}

result[len]='\0';

if (0==len)
{
sprintf(result,"%s cannot execute\n",command);
}
}
void serverItem(void *a)
{

int connectd = (int)a;
char send_buf[2048];
char recv_buf[2048];
char cmd[2048];
int recvnum;
int rvalue;
char path[2048];

bzero(cmd,10);
bzero(path,2048);

while(1)
{
memset(send_buf,0,2048);
memset(recv_buf,0,2048);
recvnum = recv(connectd,recv_buf,sizeof(recv_buf),0);

if (0>recvnum)
{
perror("recv error\n");
continue;
}

recv_buf[recvnum]='\0';

sscanf(recv_buf,"%s %s",cmd,path);
if ((0==strcmp("cd",cmd)) ||(0==strcmp("CD",cmd)))
{
chdir(path);
printf("%s",path);
continue;
}
if (0==strcmp("quit\n",recv_buf))
{
continue;
}
else
{
exec(recv_buf,send_buf);
}

rvalue=-1;
rvalue=send(connectd,send_buf,strlen(send_buf),0);
if (0>rvalue)
{
fprintf(stderr,"error in sending data\n");
continue;
}
}
}
int main(int argc,char** argv)
{
struct sockaddr_in server;
struct sockaddr_in client;
int len;
int port;
int listend;
int connectd;
int sendnum;
int opt;
int sockfd;
pthread_t thread;
//if (2==argc)

sockfd=-1;
bzero(&client,sizeof(struct sockaddr));
bzero(&server,sizeof(struct sockaddr));
len=-1;
port=PORT;
opt=SO_REUSEADDR;

if (-1==(listend=socket(AF_INET,SOCK_STREAM,0)))
{
perror("create listen socket error\n");
exit(1);
}
setsockopt(listend,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

#ifdef DEBUG
printf("the listen id is %d\n",listend);
#endif

memset(&server,0,sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl(INADDR_ANY);
server.sin_port = htons(port);

if (-1==bind(listend,(struct sockaddr *)&server,sizeof(struct sockaddr)))
{
perror("bind error\n");
exit(1);
}

if (-1==listen(listend,5))
{
perror("listen error\n");
exit(1);
}
len = sizeof(struct sockaddr);

while (1)
{
if (-1==(connectd=accept(listend,(struct sockaddr*)&client,&len)))
{
perror("create connect socket error\n");
continue;
}
pthread_create(&thread,NULL,(void*)serverItem,(void *)connectd);
}
close(listend);
return 0;

}

运行结果,当非正常关闭客户端时,服务器会出现断开的通道,





首先,自己上网搜了一下Socked send函数的用法,

要注意send函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除send外的Socket函数在执行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回SOCKET_ERROR)

注意:在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

上面一段文字来自:http://www.cnblogs.com/cgun/archive/2010/05/25/1743636.html

也正是这个原因才出现了上面所说的断开的通道的错误。

我调试程序发现当我们关闭客户端的窗口时,服务器仍会接收到一连串的东西,当服务器端接收到来自客户端的命令时(当然这不是真正有意义的命令),服务器就会响应客户端,通过send函数把buf中的数据copy到server的发送缓冲区它就返回了。由于此时c/s连接已经断开,这些数据并没有被发送出去。接着服务器接收下一个指令,并继续调用send函数去响应客户端,由于上次东西没被发送出去,所以就出现了SOCKET_ERROR,并终止了当前进程,从而出现了断开的通道。

解决方法很简单:其实也是本人了解太少了,,如果连接断开recv()函数会返回一个0,我们对其进行处理就行了。

else if(0==recvnum)
break;

貌似和send没关,但这个错误却促使我去认真了解了send函数
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: