Chinaunix

标题: 【求助】socket编程,TCP连接 [打印本页]

作者: xinuaile2003    时间: 2009-03-05 13:48
标题: 【求助】socket编程,TCP连接
大家好,我现在遇到了一个问题,请大家帮忙看下。

我使用socket编程,TCP连接,做服务器。在客户端连接上之后,只要客户端退出,服务器端的软件也随之终止,这个问题该如何解决?

代码如下:


while (1)
{
  if ((netlistenfd = InitSndSocket(&serverinfo,port)) > 0) //函数在下方
  {
   break;
  }
}
SetBlock_Srv(netlistenfd,1);//函数在下方
if (bind(netlistenfd,(struct sockaddr*)&serverinfo,sizeof(serverinfo))<0)
{
   printf("bind to port %d failure!\n",port);
   exit(-1);
   }
if (listen(netlistenfd,LISTENQUEUE) < 0)
{
   printf("call listen failure!\n");
   exit(-1);
}
   
DBG("Entering netlisten main loop.\n");
while(1)
{
  clientinfolength = sizeof(clientinfo);
  printf("listening......................\n");
  netlistenclifd = accept(netlistenfd,(struct sockaddr*)&clientinfo,&clientinfolength);
  printf("accept link : socket id:%d\n",netlistenclifd);
  if (netlistenclifd < 0)
  {
       printf("error comes when call accept!\n");
  }
  else
  {
    if (pthread_create(&netsndThread, &pattr, netsndThrFxn, &netsndEnv)) //创建一个新线程处理,线程函数在本楼底部和第9楼都给了
   {
    ERR("Failed to create video thread\n");
    cleanup(EXIT_FAILURE);
   }
  }
}



int InitSndSocket(struct sockaddr_in *serverinfo,  int port)
{
int socketid = 0;
struct sockaddr_in  serverfd;
// UDP Sockets
if((socketid=socket(AF_INET,SOCK_STREAM,0))<0)      //SOCK_DGRAM
{
  fprintf(stderr,"InitSndSocket() create socket: %d FAIL\n",socketid);
  return(-1);
}
/// Initialize Socket Informations
memset((char *)&serverfd,0,sizeof(serverfd));
serverfd.sin_family=AF_INET;
serverfd.sin_addr.s_addr = htons(INADDR_ANY);//tcp server
serverfd.sin_port=htons(port);
*serverinfo = serverfd;
return(socketid);
}


int    SetBlock_Srv(int fd,int flag)
{
int    flags;
if((flags=fcntl(fd,F_GETFL,0))==-1)
{
  perror("fcntl");
  return -1;
}
if(flag==0)
{
  // Non Block Socket
  fcntl(fd,F_SETFL,flags|O_NDELAY);
}
else if(flag==1)
{
  // Block Socket
  fcntl(fd,F_SETFL,flags&~O_NDELAY);
}
return 0;
}

void        *netsndThrFxn(void *arg)
{
        NetsndBufferElement wFlush        = { NETSND_FLUSH };
        NetsndEnv          *envp          = (NetsndEnv *) arg;
        void               *status        = THREAD_SUCCESS;
        NetsndBufferElement we;
        int i = 0,k = 0;
        int netsndclifd = envp->socketid;
        DBG("Message Send Thread Start\n");

        DBG("Entering netsnd main loop.\n");

        /// Message Send Loop
        while (1)
        {

                if (FifoUtil_get(&envp->inFifo, &we) == FIFOUTIL_FAILURE) //获取数据,用于发送
                {        
                        ERR("fail:FifoUtil_get\n");
                        breakLoop(THREAD_FAILURE);
                }

                if (we.id == NETSND_FLUSH)
                {
                        ERR("fail:NETSND_FLUSH\n");
                        breakLoop(THREAD_SUCCESS);
                }
               
                if (we.id != NETSND_PRIME && we.frameSize)
                {
                        DBG("frameSize:%d\n",we.frameSize);
                        k = we.frameSize / 1400;
                        if(k)
                        {
                                for(i = 0;i < k;i++)
                                {
                                        if ( send(netsndclifd, (char *)(we.encodedBuffer + i * 1400), 1400, 0) < 0 )
                                        {
                                                /* send error */
                                                ERR("Error send the encoded data to network\n");
                                        }
                                }
                        }
                        if ( send(netsndclifd, (char *)(we.encodedBuffer + k * 1400), we.frameSize - k * 1400, 0) < 0 )
                        {
                                /* send error */
                                ERR("Error send the encoded data to network\n");
                        }
                }
                else
                {
                        we.id = NETSND_NORMAL;
                }

                if (FifoUtil_put(&envp->outFifo, &we) == FIFOUTIL_FAILURE) //通知其他线程,数据已经读取
                {
                        ERR("Failed to put buffer in output fifo\n");
                        breakLoop(THREAD_FAILURE);
                }

        }
        
        return status;
}

[ 本帖最后由 xinuaile2003 于 2009-3-6 08:52 编辑 ]
作者: dreamice    时间: 2009-03-05 13:53
标题: 回复 #1 xinuaile2003 的帖子
这是win下的东东,函数接口看起来怪怪的
作者: xinuaile2003    时间: 2009-03-05 13:58
原帖由 dreamice 于 2009-3-5 13:53 发表
这是win下的东东,函数接口看起来怪怪的


linux下的,使用c编写的
作者: alexhappy    时间: 2009-03-05 14:35
对客户端退出事件做处理
作者: 墨小白    时间: 2009-03-05 17:17
客户断开连接会有一个close socket
而你这边会有一个信号,当收到这个信号你就可以做出处理的
作者: Cyberman.Wu    时间: 2009-03-05 19:59
我怀疑是你创建的服务器纯种在客户端关闭时处理上有问题吧,刚一个线程非法退出(产生Segmentation fault)时就会导致整个进程终止。你给的代码不全也看不出啥来。
作者: xinuaile2003    时间: 2009-03-06 08:18
原帖由 墨小白 于 2009-3-5 17:17 发表
客户断开连接会有一个close socket
而你这边会有一个信号,当收到这个信号你就可以做出处理的



那怎么获取这个close socket的信号呢?
作者: xinuaile2003    时间: 2009-03-06 08:19
原帖由 Cyberman.Wu 于 2009-3-5 19:59 发表
我怀疑是你创建的服务器纯种在客户端关闭时处理上有问题吧,刚一个线程非法退出(产生Segmentation fault)时就会导致整个进程终止。你给的代码不全也看不出啥来。



我也感觉是线程非法退出导致的,可是找不到头绪。
作者: xinuaile2003    时间: 2009-03-06 08:50
我把创建的线程代码也放上去吧


void        *netsndThrFxn(void *arg)
{
        NetsndBufferElement wFlush        = { NETSND_FLUSH };
        NetsndEnv          *envp          = (NetsndEnv *) arg;
        void               *status        = THREAD_SUCCESS;
        NetsndBufferElement we;
        int i = 0,k = 0;
        int netsndclifd = envp->socketid;
        DBG("Message Send Thread Start\n");

        DBG("Entering netsnd main loop.\n");

        /// Message Send Loop
        while (1)
        {

                if (FifoUtil_get(&envp->inFifo, &we) == FIFOUTIL_FAILURE) //获取数据,用于发送
                {        
                        ERR("fail:FifoUtil_get\n");
                        breakLoop(THREAD_FAILURE);
                }

                if (we.id == NETSND_FLUSH)
                {
                        ERR("fail:NETSND_FLUSH\n");
                        breakLoop(THREAD_SUCCESS);
                }
               
                if (we.id != NETSND_PRIME && we.frameSize)
                {
                        DBG("frameSize:%d\n",we.frameSize);
                        k = we.frameSize / 1400;
                        if(k)
                        {
                                for(i = 0;i < k;i++)
                                {
                                        if ( send(netsndclifd, (char *)(we.encodedBuffer + i * 1400), 1400, 0) < 0 )
                                        {
                                                /* send error */
                                                ERR("Error send the encoded data to network\n");
                                        }
                                }
                        }
                        if ( send(netsndclifd, (char *)(we.encodedBuffer + k * 1400), we.frameSize - k * 1400, 0) < 0 )
                        {
                                /* send error */
                                ERR("Error send the encoded data to network\n");
                        }
                }
                else
                {
                        we.id = NETSND_NORMAL;
                }

                if (FifoUtil_put(&envp->outFifo, &we) == FIFOUTIL_FAILURE) //通知其他线程,数据已经读取
                {
                        ERR("Failed to put buffer in output fifo\n");
                        breakLoop(THREAD_FAILURE);
                }

        }
       
        return status;
}
作者: 墨小白    时间: 2009-03-06 09:57
标题: 回复 #7 xinuaile2003 的帖子
我说的是正常退出情况下  recv函数会返回0

对于非法退出的话 可以设置一个心跳机制

client每间隔M秒发一个心跳包给server
若连续N秒server端没有收到client的心跳包,则认为client已经去见马克思了

[ 本帖最后由 墨小白 于 2009-3-6 10:09 编辑 ]
作者: xinuaile2003    时间: 2009-03-06 10:43
原帖由 墨小白 于 2009-3-6 09:57 发表
我说的是正常退出情况下  recv函数会返回0

对于非法退出的话 可以设置一个心跳机制

client每间隔M秒发一个心跳包给server
若连续N秒server端没有收到client的心跳包,则认为client已经去见马克思了



我的客户端使用的是ie浏览器,是改不了了
作者: Cyberman.Wu    时间: 2009-03-06 14:12
标题: 回复 #9 xinuaile2003 的帖子
你这样这次露出个脚丫子、下次露出个手指头的有谁看得懂啊

总要把一些关键给别人看才能帮你啊。你用GDB测试一下,或者在代码中加上捕获异常信号的处理看一看。不明白的是你的线程函数的入口参数是啥,malloc的,还是?
作者: Cyberman.Wu    时间: 2009-03-06 14:18
标题: 回复 #11 xinuaile2003 的帖子
如果你做WEB服务器的话有一些标准的做法的,如果客户端用connection:close,或你想强制用这种方式,发完了简单关闭socket就可以了;如果想用keep-alive,一般是发完数据之后等一会儿,看客户端会不会在同一个socket上再次请求,如果请求继续执行,否则关闭连接。
作者: FreeB_U    时间: 2009-03-09 13:34
好好看看STEVEN的网络编程第一卷,前几张,由浅入深,一步一步改造你的程序到最佳状态。上面都有。
作者: xinuaile2003    时间: 2009-03-13 10:51
把send(netsndclifd, (char *)(we.encodedBuffer + i * 1400), 1400, 0)改成
send(netsndclifd, (char *)(we.encodedBuffer + i * 1400), 1400, MSG_NOSIGNAL),现在是不会自动退出了,只是不知道会不会出现什么后遗症。
作者: liying_gg    时间: 2009-03-13 10:55
给你一个正确的serverside。
/* serverside.c - a simple server application */

/* Copyright 1984-2006 Wind River Systems, Inc. */

/*
modification history
--------------------
01a,12oct06,d_c   written.
*/

/* include */

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

/* globals */

/* message to be sent to client on a connect */
const char MESSAGE[] = "Hello, World!\n";

/* set the maximum length the queue of pending connections may grow to */
const int BACK_LOG = 5;

int main(int argc, char *argv[])
        {
        int serverSocket = 0;
        int enablePortReuse = 0;
        int serverPort = 0;
        int status = 0;
        int childPid = 0;
        char hostname[80] = "";

        struct hostent *hostPtr = NULL;
        struct sockaddr_in serverName = { 0 };
        struct linger linger = { 0 };

        /* read & validata input parameters */
        if (argc != 2)
                {
                fprintf(stderr, "Usage: serverside <port>\n");
                exit(-1);
                }

        serverPort = atoi(argv[1]);

        if (serverPort < 0)
                {
                fprintf(stderr, "serverside: strange serverPort value specified\n");
                exit (-1);
                }

        if ( (serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1 )
                {
                fprintf(stderr, "serverside: error calling socket()\n");
                exit (-1);
                }

        /* setup and enable listening */
        enablePortReuse = 1;

        if ( (status = setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR,
                (const char *) &enablePortReuse, sizeof(enablePortReuse))) == -1)
                {
                fprintf(stderr, "serverside: warning, setting SO_REUSEADDR failed\n");
                }

        linger.l_onoff = 1;
        linger.l_linger = 30;

        if ( (status = setsockopt(serverSocket, SOL_SOCKET, SO_LINGER, (const char *) &linger,
                sizeof(linger))) == -1)
                {
                fprintf(stderr, "serverside: warning, setting SO_LINGER failed\n");
                }

        if ( (status = gethostname(hostname, sizeof(hostname))) == -1 )
                {
                fprintf(stderr, "serverside: error calling gethostname()\n");
                exit (-1);
                }

        if ( (hostPtr = gethostbyname(hostname)) == NULL )
                {
                fprintf(stderr, "serverside: error calling gethostbyname(%s)\n", hostname);
                exit (-1);
                }

        (void) memset(&serverName, 0, sizeof(serverName));
        (void) memcpy(&serverName.sin_addr, hostPtr->h_addr, hostPtr->h_length);

        serverName.sin_family = AF_INET;
        serverName.sin_port = htons(serverPort);

        if ( (status = bind(serverSocket, (struct sockaddr *) &serverName, sizeof(serverName))) == -1 )
                {
                fprintf(stderr, "serverside: error calling bind()\n");
                exit (-1);
                }

        if ( (status = listen(serverSocket, BACK_LOG)) == -1 )
                {
                fprintf(stderr, "serverside: error calling listen()\n");
                exit (-1);
                }

        /* wait for connection */

        while (1)
                {
                struct sockaddr_in clientName = { 0 };
                int clientSocket = 0;
                int clientLength = sizeof(clientName);

                (void) memset(&clientName, 0, sizeof(clientName));

                if ( (clientSocket = accept(serverSocket, (struct sockaddr *) &clientName,
                        &clientLength)) == -1 )
                        {
                        fprintf(stderr, "serverside: error calling accept()\n");
                        exit (-1);
                        }

                /* for each connection accepted, start a new process. this way we can
                   accept multiple connections */
                childPid = fork();

                switch (childPid)
                        {
                        case -1:

                                fprintf(stderr, "serverside: error calling fork()\n");
                                exit (-1);

                        case 0:

                                /* the child process. this is where we reply to the
                                   client's message */
                                close(serverSocket);

                                /* insert on_line() */ write(clientSocket, MESSAGE, strlen(MESSAGE));

                                close(clientSocket);

                                /* close this child process as task is done */
                                exit(0);

                        default:

                                /* the parent process */
                                close(clientSocket);
                        }
                }

        return 0;
        }
作者: liying_gg    时间: 2009-03-13 10:55
在给你一个正确的clientside

/* clientside.c - a simple client application for the server */

/* Copyright 1984-2006 Wind River Systems, Inc. */

/*
modification history
--------------------
01a,12oct06,d_c   written.
*/

/* include */

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

/* defines */

#ifndef NSEC_PER_SEC
#define NSEC_PER_SEC 1000000000L
#endif
#define NUM_SAMPLES 1000

typedef unsigned long nsec_t;

nsec_t getNsecTimeDiff(struct timespec *before_time, struct timespec *after_time);
nsec_t timespecToNs(struct timespec *ts);

int main(int argc, char *argv[])
        {
        int clientSocket = 0;
        int serverPort   = 0;
        int status   = 0;
        char buffer[16]  = "";
        char *serverHost = NULL;
        nsec_t nsecDiff = 0;
        nsec_t average = 0;
        int count = 0;

        struct hostent *hostPtr = NULL;
        struct sockaddr_in serverName = { 0 };
        struct timespec before_time;
        struct timespec after_time;

        /* read & validata input parameters */
        if (argc != 3)
                {
                fprintf(stderr, "Usage: clientside <serverHost> <serverPort>\n");
                exit (-1);
                }

        serverHost = argv[1];
        serverPort = atoi(argv[2]);

        if (strlen(serverHost) < 1)
                {
                fprintf(stderr, "clientside: strange serverHost value specified\n");
                exit (-1);
                }

        if (serverPort < 0)
                {
                fprintf(stderr, "clientside: strange serverPort value specified\n");
                exit (-1);
                }

        /* count starts from 1 so we can start calculating average */
        for (count = 1; count <= NUM_SAMPLES; count++)
                {
                /* setup and enable sending */
                if ( (clientSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1 )
                        {
                        fprintf(stderr, "clientside: error opening socket\n");
                        exit (-1);
                        }


                if ( (hostPtr = gethostbyname(serverHost)) == NULL)
                        {
                        if ( (hostPtr = gethostbyaddr(serverHost, strlen(serverHost),
                                AF_INET)) == NULL )
                                {
                                fprintf(stderr, "clientside: error resolving server address\n");
                                exit (-1);
                                }
                        }

                serverName.sin_family = AF_INET;
                serverName.sin_port = htons(serverPort);

                (void) memcpy(&serverName.sin_addr, hostPtr->h_addr,
                        hostPtr->h_length);

                /* start timing operations */
                clock_gettime(CLOCK_REALTIME, &before_time);

                if ( (status = connect(clientSocket, (struct sockaddr*) &serverName,
                        sizeof(serverName))) == -1 )
                        {
                        fprintf(stderr, "clientside: error connecting %d\n", count);
                        exit (-1);
                        }

                while (0 < (status = read(clientSocket, buffer, sizeof(buffer) - 1)));

                if (status == -1)
                        {
                        fprintf(stderr, "clientside: error reading\n");
                        exit (-1);
                        }

                clock_gettime(CLOCK_REALTIME, &after_time);

                /* stop timing operations */

                nsecDiff = getNsecTimeDiff(&before_time, &after_time);

                average = ( (average * (count - 1) ) + nsecDiff ) / count ;

                close(clientSocket);
                }
        printf("%s",buffer);
        printf("avg for %d samples is 0x%lx ns or %lu ns\n", NUM_SAMPLES, average, average);

        return 0;
}

nsec_t getNsecTimeDiff(struct timespec *before_time, struct timespec *after_time)
        {
        nsec_t nsecDiff = timespecToNs(after_time) - timespecToNs(before_time);

        return nsecDiff;
        }

nsec_t timespecToNs(struct timespec *ts)
        {
        return ((nsec_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
        }
作者: xinuaile2003    时间: 2009-03-13 11:37
原帖由 liying_gg 于 2009-3-13 10:55 发表
给你一个正确的serverside。
/* serverside.c - a simple server application */

/* Copyright 1984-2006 Wind River Systems, Inc. */

/*
modification history
--------------------
01a,12oct06,d ...




你使用的是创建一个进程(fork),而不是一个线程(phtread)。socket发送数据可以使用send函数不?

我的系统中有好几个线程呢,在其中一个线程使用fork函数,是不是会在子进程中创建所有的线程呢?
作者: FreeB_U    时间: 2009-03-13 12:05
服务器上处理errno=EINTR
作者: Cyberman.Wu    时间: 2009-03-14 00:58
原帖由 xinuaile2003 于 2009-3-13 11:37 发表




你使用的是创建一个进程(fork),而不是一个线程(phtread)。socket发送数据可以使用send函数不?

我的系统中有好几个线程呢,在其中一个线程使用fork函数,是不是会在子进程中创建所有的线程呢?


按APUE中12.9的说法fork只会创建调用fork的那个线程。其实这个很容易测试的,你测试一下就知道了。
作者: xinuaile2003    时间: 2009-03-14 11:10
原帖由 Cyberman.Wu 于 2009-3-14 00:58 发表


按APUE中12.9的说法fork只会创建调用fork的那个线程。其实这个很容易测试的,你测试一下就知道了。




谢谢你的帮忙,我会测试的。




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2