• 200行代码打造一个socket聊天室


    服务端

    运行环境: centos7
    编译命令: g++ server.cpp -o server -lpthread(链接pthread线程动态链接库)

    #include <stdio.h>
    #include <errno.h>
    #include <stdbool.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <string.h>
    #include <pthread.h>
    
    int arr[100];
    
    int host = 0;
    
    struct parameter
    {
        int clifd;                //arg1
        int cnt;                  //arg2
    };
    
    bool is_exist(int fd)                     //遍历检测客户端�
    {
    	int i;
    	for(i=0; arr[i]; i++)
    	{
    		// printf("arr[%d] = %d
    ",i,arr[i]);
    		if(fd == arr[i]) return true;
    	}
    	return false;
    }
    
    
    bool IsSocketClosed(int clientSocket)     //TCP心跳检测
    {
    	char buff[32];
    
     	int recvBytes = recv(clientSocket, buff, sizeof(buff), MSG_PEEK); 
    
     	int sockErr = errno;
    
    	// printf("sockErr = %d",sockErr);
    
     	if( recvBytes > 0)		//Get data
    		return false;
    
    
    
     	if( (recvBytes == -1) && (sockErr == EWOULDBLOCK) ) //No receive data 
     	return false;
    
     	return true;
    }
    
    
    void* start_run(void* arg)	     //泛型指针变量
    {
    	struct parameter *p =(parameter*)arg;
    	int i;
    	int clifd = *(int*)arg;
    	int cnt = p->cnt;
    
    	printf("%d
    ", cnt);
    
    	char buf[1024] = {};
    
    	// printf("%d
    ", strlen(arr));
    
    
    	while(true)
    	{
    
    		recv(clifd,buf,sizeof(buf),0);
    		char* prefix = "  当前在线人数【";
    		char* suffix = "】";
    		if (strlen(buf) != 0)
    		{
    			sprintf(buf,"%s%s%d%s",buf,prefix,cnt,suffix);
    			printf("%s
    ", buf);
    			if(strlen(buf) != 0)
    			{
    				printf("%s
    ",buf);
    			}
    		}
    
    		for(i=0; arr[i]; i++)
    		{
    			if(arr[i] != clifd)
    			{
    				// char* buf = (char*) malloc(strlen(buf) +strlen(cnt));
    
    				send(arr[i],buf,strlen(buf)+1,0);
    
    			}
    		}
    
    		if(strlen(buf) != 0 && 0 == strcmp("quit",strchr(buf, ':')+1) || IsSocketClosed(clifd))
    		{
    			host--;
    			close(clifd);
    			pthread_exit(NULL);
    		}
    
    	}
    }
    
    int main()
    {
    	printf("服务器创建socket...
    ");
    	int sockfd = socket(AF_INET,SOCK_STREAM,0);
    	if(0 > sockfd)
    	{
    		perror("socket");
    		return -1;
    	}
    
    	printf("准备地址...
    ");
    	struct sockaddr_in addr = {};
    	addr.sin_family = AF_INET;
    	addr.sin_port = htons(7767);
    	addr.sin_addr.s_addr = inet_addr("172.17.109.66");
    	socklen_t len = sizeof(addr);
    
    	printf("绑定socket与地址...
    ");
    	if(bind(sockfd,(struct sockaddr*)&addr,len))
    	{
    		perror("bind");
    		return -1;
    	}
    
    	printf("设置监听...
    ");
    	if(listen(sockfd,5))
    	{
    		perror("listen");
    		return -1;
    	}
    
    	struct parameter *par =  new parameter;
    
    	int cnt = 0;
    
    
    	printf("等待客户端连接...
    ");
    	while(true)
    	{
    		struct sockaddr_in addrcli = {};
    		int clifd = accept(sockfd,(struct sockaddr*)&addrcli,&len);  
    		if(0 > clifd)
    		{
    			perror("accept");
    			continue;
    		}
    		if(!is_exist(clifd))
    		{
    			arr[cnt] = clifd;
    			// printf("cnt = %d, clifd = %d
    ",cnt,clifd);
    			cnt++;
    		}
    
    
    		par->clifd = clifd;
    		par->cnt = cnt;
    
    		pthread_t pid;
    
    		int flag = pthread_create(&pid, NULL, start_run, (void*)par);
    		host++;
    
    		if (flag == -1)
    		{
    			/* code */
    			printf("create error!
    ");
    	        return 1;
    		}
    
    	}
    }
    
    

    客户端

    运行环境: window cmd
    编译环境: clion + mingw64
    动态链接dll库: 生成exe文件需将libstdc++-6.dll,libwinpthread-1.dll与exe文件置于同一文件目录下已备exe文件调用。

    #include <stdio.h>
    #include <string.h>
    #include <pthread.h>
    #include <Windows.h>
    #include <WinSock2.h>
    #pragma comment(lib, "ws2_32.lib")
    #pragma comment(lib, "pthreadVC2.lib")               //链接动态线程库
    
    #define ONEBYTE 1024
    
    struct parameter
    {
        int sktCli;//参数1
        char* name;//参数2
    };
    
    void* start_send(void* arg)              //发送聊天消息
    {
    
        struct parameter *p =(parameter*)arg;
        int sockfd = *(int*)arg;
        char buf[ONEBYTE] = {};
        char buf1[ONEBYTE] = {};
        while (true)
        {
    //        char* prefix = ">>>";
            gets(buf);
    //        sprintf(buf,"%s%s",prefix,buf);
            sprintf(buf1, "%s:%s", p->name, buf);
            send(sockfd,buf1,strlen(buf1)+1,0);     //数据写入
            if(0 == strcmp("quit",buf))            //quit退出聊天室
            {
                printf("***退出聊天室***
    ");
                pthread_exit(NULL);                //结束线程
            }
        }
    }
    
    void* start_recv(void* arg)
    {
        int sockfd = *(int*)arg;
        char buf[ONEBYTE] = {};
        while(true)
        {
            recv(sockfd,buf,sizeof(buf),0);//读取缓冲区数据
            printf("%s
    ",buf);
        }
    }
    
    
    int main(int argc, char *argv[]) {
    
        //初始化DLL
        WSADATA wd;
        WSAStartup(MAKEWORD(2, 2), &wd);
    
        //创建客户端套接字
        int sktCli = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    
        sockaddr_in addrSer = {0};
        addrSer.sin_family = AF_INET;                                        //设置IPV4协议
        addrSer.sin_port = htons(7767);                            //设置服务器的端口
        addrSer.sin_addr.s_addr = inet_addr("47.94.170.97");             //设置服务器公网IP
        //连接服务器
        connect(sktCli, (sockaddr *)&addrSer, sizeof(addrSer));
    
    
    
        struct parameter *par =  new parameter;
    
        puts("***欢迎来到十三月的聊天室***");
        printf("Tips:退出请输入quit
    ");
        printf("少侠请留名:");
    
        char name[20] = {};//昵称初始化
    
        gets(name);
    
        if (strlen(name) == 0)
        {
            /* code */
            strcpy(name,"无名氏");
        }
    
        par->sktCli =  sktCli;
        par->name = name;
    
        pthread_t pid;
        pthread_create(&pid, NULL, start_recv, &sktCli);               //创建接收消息线程
        pthread_create(&pid, NULL, start_send, (void*)par);               //创建发送消息线程
        void* p = NULL;
        pthread_join(pid, &p);                                              //主线程等待pid1线程
    
    
        WSACleanup();
        return 0;
    }
    

    已编译打包好的客户端文件

    链接:https://pan.baidu.com/s/1BIimAH9GRI9XuaKrTfm4jQ 
    提取码:3tbq
    
  • 相关阅读:
    链表问题----反转部分单向链表
    HTTP请求详解
    链表问题----删除链表的中间节点和a/b处的节点
    链表问题----删除倒数第K个节点
    栈和队列----最大值减去最小值小于等于num的子数组的数量
    栈和队列----求最大子矩阵的大小
    TCP/IP、Http、Socket的区别
    栈和队列----生成窗口的最大值数组
    linux根文件系统制作,busybox启动流程分析
    linux 内核启动流程分析,移植
  • 原文地址:https://www.cnblogs.com/shisanyue/p/13510085.html
Copyright © 2020-2023  润新知