聊天小程序(五)客户端退出

需求:客户端断开连接后,能保证下次连接正常。

解决方法:当客户端断开后,服务端从链表中删除该节点。当客户端重新连接时,则重新插入到链表中。

server.c:

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

#include "base.h"

#define LISTENQ 2       //最大监听队列
#define PORT 5000        //监听端口
#define MAXFD 2          //最大的在线用户数量

static int maxi = 0;       //maxi表示当前client数组中最大的用户的i值

typedef struct sockNode SOCKNODE;
struct sockNode
{
    char name[128]; // 存储名字
    int fd;          // 存储套接字
    struct sockNode *pNext;  
};

SOCKNODE sockList; // 全局的套接字链表

void initList(SOCKNODE *sockList)
{
    sockList = (SOCKNODE *)malloc(sizeof(SOCKNODE));
    sockList->pNext = NULL;
    sockList->fd = 0;
}

void insertList(SOCKNODE *sockList, int new_fd) //头部插入链表
{
    int flags;
    SOCKNODE *p = sockList;
    SOCKNODE *sockNew = (SOCKNODE *)malloc(sizeof(SOCKNODE));

    // 设置非阻塞方式
	flags = fcntl(new_fd, F_GETFL, 0);
    fcntl(new_fd, F_SETFL, flags | O_NONBLOCK); 

    sockNew->fd = new_fd;
    sockNew->pNext = NULL;

    while(p->pNext != NULL)
    {
        p = p->pNext;
    }
    p->pNext = sockNew;

}

void transmit(SOCKNODE *sock, message mess)
{
    SOCKNODE *sockNew = &sockList;
	sockNew = sockNew->pNext; // 第一个节点	
	char buffer[128];
	// 添加昵称到转发字符串中
	memcpy(buffer, sock->name, sizeof(sock->name));
	strcat(buffer, " : ");
	strcat(buffer, mess.buffer);
	memcpy(mess.buffer, buffer, sizeof(buffer));
	
	while(sockNew->fd != sock->fd || sockNew->pNext != NULL) //相邻
	{
	    if(sockNew->fd == sock->fd && sockNew->pNext != NULL) // 满足while第一个条件,跳过,第二条件,执行
	         sockNew = sockNew->pNext;
	         
        mess.sig_event = SIG_TRASMIT_MESSAGE;
	    send(sockNew->fd, (char *)&mess, sizeof(mess),0);

	    if(sockNew->pNext == NULL)// 到达末尾
	        break;
	    else
	        sockNew = sockNew->pNext;
    }
}

void exitClient(SOCKNODE *sock)
{
    message mess;
    // 释放链表中的节点
    SOCKNODE *sockNew = &sockList;
    SOCKNODE *sockNode;
    while(sockNew->pNext != NULL)
    {
        if(sockNew->pNext->fd == sock->fd)
        {
            sockNode = sockNew->pNext;
            sockNew->pNext = sockNode->pNext;
            free(sockNode);
            break;
        }
        sockNew = sockNew->pNext;
    }
    mess.sig_event = SIG_CLIENT_QUIT_SUCCEED;
    if((send(sock->fd, (char *)&mess, sizeof(mess), 0))==-1)
    {
        printf("[%s] 's socket [%d] exit failed!\n", sock->name, sock->fd);
    }
    maxi = maxi - 1;
    if(maxi < 0)
        exit(1);

}
void recvandsend(void)           //监听转发线程入口函数
{
    int index=0;
    int nbytes=0;
    char mess_buff[2048];
    message mess;
    int  len;
    int  outindex=0;
    printf("receive socket and return message!\n");
	while(1)
    {
        SOCKNODE *sock = &sockList;
        while(sock->pNext != NULL)
        {
            sock = sock->pNext;
	        nbytes = 0;
	        memset(mess_buff, 0, sizeof(mess_buff));
	        nbytes = recv(sock->fd, mess_buff, sizeof(mess_buff),0);
	        if(nbytes > 0) // 在某个套接字有消息进来
	        {
	            memcpy(&mess, mess_buff, sizeof(mess_buff));
	            if(mess.sig_event == SIG_CLIENT_SET_NAME)
	            {
	                memcpy(sock->name, mess.buffer, strlen(mess.buffer)); // 将昵称保存到节点中
	                printf("set client [%d] name : [%s]\n", sock->fd, sock->name);
	                continue;
	            }
	            if(mess.sig_event == SIG_TRASMIT_MESSAGE)
	            {
	                printf("receive [%s] message : [%s]\n", sock->name, mess.buffer);
	                transmit(sock, mess); // 消息转发
	                continue;
	            }
	            if(mess.sig_event == SIG_CLIENT_QUIT)
	            {
	                printf("close [%s] 's socket [%d], quit!\n",sock->name, sock->fd);
	                exitClient(sock);
	                continue;
	            }
	        }
	    }
    }
    pthread_exit(NULL);
    exit(1);
}

int  closeConnection(int sockfd)
{
    message mess;
    memcpy(mess.buffer, "server refused client connecte!", sizeof("server refused client connecte!"));
    mess.sig_event = SIG_REFUSE_CLIENT_CONNECT;
    printf("refuse client connecte! ");
    if((send(sockfd, (char *)&mess, sizeof(mess), 0)) == -1)
        return 1;
    else
        return 0;
}

int  connectSucceeddMess(int sockfd)
{
    message mess;
    int sendBytes;

    memcpy(mess.buffer, "SERVER : welecome to this chat room!", sizeof("SERVER : welecome to this chat room!")); 
    mess.sig_event = SIG_CLIENT_CONNECT_SUCCEED;
       
    printf("%d socked connected succeed , return message : [%s]\n", sockfd, mess.buffer);
    
    sendBytes = send(sockfd, (char *)&mess, sizeof(mess), 0);
    printf("send bytes = [%d]\n", sendBytes);
    if(sendBytes == -1 || sendBytes == 0)
        return 1;
    else
        return 0;
}

int main(int argc, char *argv[])
{
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    int        sin_size;
    int        sockfd, new_fd;
    int        thr_id;         /* thread ID for the newly createdthread */
    pthread_t  p_thread;       /* thread's structure                     */
    SOCKNODE *sockNew;
    char buffer[1024];
    int nbytes=0;
    int flags;
    initList(&sockList); // 初始化套接字链表
     
    if(argc != 1)
    {
        fprintf(stderr,"Usage:%sportnumber\a\n",argv[0]);
        exit(1);
    }
 
    /* 服务器端开始建立 socket 描述符 */
    if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1)
    {
        fprintf(stderr,"Socketerror:%s\n\a",strerror(errno));
        exit(1);
    }
 
    /* 服务器端填充 sockaddr 结构 */
    bzero(&server_addr,sizeof(struct sockaddr_in));
    server_addr.sin_family=AF_INET;
    server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
 
    server_addr.sin_port=htons(PORT);
    /* 捆绑 sockfd 描述符 */
    if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
    {
        fprintf(stderr,"Binderror:%s\n\a",strerror(errno));
        exit(1);
    }
    printf("Server Port : %d ... ...\n",PORT);

    /* 监听 sockfd 描述符 */
    if(listen(sockfd,LISTENQ)==-1)
    {
        fprintf(stderr,"Listenerror:%s\n\a",strerror(errno));
        exit(1);
    }
    
   thr_id = pthread_create(&p_thread, NULL, recvandsend, NULL); 
    
    while(1)
    {
        // 服务器阻塞,直到客户程序建立连接
        sin_size = sizeof(struct sockaddr_in);
        new_fd = accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size);
        if(maxi >= MAXFD)
        {
            if((new_fd != -1) && (closeConnection(new_fd) == 0))
		    {
		        close(new_fd);
		        printf("close %d connection success\n",new_fd);
		    }
        }
        else
        {
            if(new_fd == -1)
	        {
	            printf("socket Error!\n");
	            exit(1);
	        }
	        else
	        {
	            if(!connectSucceeddMess(new_fd))
	            {   
	                insertList(&sockList, new_fd); // 新套接字插入到链表中
	                maxi = maxi + 1;
	                printf("add %d to list succeed!\n", new_fd);
		        }
	        }
	    }
	   
    }
    close(sockfd);
    exit(0);
}

client.c:

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

#include "base.h"

#define TRUE 1
#define PORT 5000
 
static int sockfd;
static int set_name_falg = 1;
char name[100];

void closeClient()
{
    char mes[2048];
    int re;
    recv(sockfd, mes, sizeof(mes), 0);
    re = strncmp(mes, "close", sizeof("close"));
	printf("%s\n", mes); 
    if(re == 0)
    {
        printf("will colse socke, and exit client!\n");
        exit(1);
    }
}
void* recvfromserver()        //接受服务器消息线程入口函数
{
    message mess;
    int nbytes=0;
    int re;
    char mess_buf[2048];
    printf("ready to receive message!\n");
    while(1)
    {
        memset(mess_buf, 0, sizeof(mess_buf));
        nbytes = recv(sockfd, mess_buf, sizeof(mess_buf), 0);
        if(nbytes <= 0 )
            continue;
        
        memcpy(&mess, mess_buf, sizeof(mess_buf));

        if(mess.sig_event == SIG_SET_NAME_SUCCEED)
            printf("set name succeed!\n");
        if(mess.sig_event == SIG_CLIENT_CONNECT_SUCCEED)
        {
            printf("connected succeed!, \nreceive message from server :[%s]\n", mess.buffer);

            set_name_falg = 0;  // 连接成功后,设置称呼
        }
        if(mess.sig_event == SIG_TRASMIT_MESSAGE)
        {
            printf("[%s]\n",mess.buffer);
        }
        if(mess.sig_event == SIG_CLIENT_QUIT_SUCCEED)
        {
            printf("client exit succeed\n");
            break;
        }
        if(mess.sig_event == SIG_REFUSE_CLIENT_CONNECT)
        {
            printf("%s\n", mess.buffer);
            close(sockfd);
            break;
        }
    }
    pthread_exit(NULL);
    exit(0);
}

int  sendMessage(char *buffer, int sig_type)
{
    int sendBytes;
    message mess;

    memset(&mess, 0, sizeof(mess));
    memcpy(mess.buffer, buffer, strlen(buffer));

    mess.sig_event = sig_type; //设置昵称事件号
    sendBytes = send(sockfd, (char *)&mess, sizeof(mess), 0);
    if(sendBytes == -1 || sendBytes == 0)
        return 1;
    else
        return 0;
}


int main(int argc, char *argv[])
{
   char   buffer[1024];
   struct sockaddr_in server_addr;
   struct hostent *host;
   int    portnumber,nbytes;
   char   strhost[32];
   int    thr_id;         /* thread ID for the newly createdthread */
   pthread_t  p_thread;      // thread's structure     
   message mess;
   if(argc!=1)
   {
		fprintf(stderr,"Usage:%s\a\n",argv[0]);
		exit(1);
   }
   snprintf(strhost, 32, "%s", "10.47.181.159");
   printf("The Server IP Address : [%s]", strhost);

   if((host = gethostbyname(strhost)) == NULL)
   {
		fprintf(stderr,"Gethostnameerror\n");
		exit(1);
   }
 
   /* 客户程序开始建立 sockfd 描述符 */
   printf("Connection ... ...\n");
   if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
   {
		fprintf(stderr,"SocketError:%s\a\n",strerror(errno));
		exit(1);
   }
   /* 客户程序填充服务端的资料 */
   bzero(&server_addr,sizeof(server_addr));
   server_addr.sin_family=AF_INET;
   server_addr.sin_port=htons(PORT);
   server_addr.sin_addr=*((struct in_addr *)host->h_addr);   
   /* 客户程序发起连接请求 */
   if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
   {
		  fprintf(stderr,"ConnectError:%s\a\n",strerror(errno));
		  exit(1);
   }
   sleep(1);
   thr_id = pthread_create(&p_thread, NULL, recvfromserver, NULL);    
   sleep(1);
   printf("\nNOTICE: Press Quit or q or Q disconnect server!\n\n");
  
   while(1)
   {
       if(set_name_falg == 0)
       {
           printf("Set Your Name : ");
           gets(name);
           sendMessage(name, SIG_CLIENT_SET_NAME);
           set_name_falg = 1;
           continue;;
       }
        printf("Input message : ");
	    memset(&buffer, 0, sizeof(buffer));
        gets(buffer);
        
		if(strcmp(buffer,"Quit")==0 || strcmp(buffer,"q")==0 || strcmp(buffer,"Q")==0)
		{
		    sendMessage(buffer, SIG_CLIENT_QUIT);
			break;
		}
		if(sendMessage(buffer, SIG_TRASMIT_MESSAGE) != 0)
        {
            printf("send message error\n");
        }

	}
    close(sockfd);
    exit(0);
}
发布了91 篇原创文章 · 获赞 75 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/fengxianghui01/article/details/104458189