websocket聊天室设计,实现,测试(初步)


  


前一篇文章:《基于CSDN上 SGuniver_22的《Linux下c语言实验Websocket通讯 含客户端和服务器测试代码》的websocket聊天代码》 在此:, SGuniver_22的代码在此,为方便这里也提供 本地下载

目标/需求是建立一个交友(特别是婚恋交友)网站, (市面上已有的如佳缘/百合/珍爱网的聊天功能似乎都不是很能用,不能令笔者满意, 所以改善这一情况有必要。我希望不用建一个单个的大网站,而希望按地域建各个分布众多的地方性网站,我(们)可以给各地站长提供服务 (免费的或付费的都有可能))。

为了最便于部署(希望每一个县市(地域)范围有人可以部署运营一个),本系统拟不采用数据库!

这里介绍聊天功能部分的开发。websocket服务器代码许可证拟采用GPL v2 or later,保留扩大许可证的权利。

最初版本只用了一个buffer,是否适用待观察。

design_1.jpg
design_2.jpg
design_25.jpg
design_3.jpg
design_1.jpg
登录页面

design_2.jpg
主(main)页面:
(1)显示快照所有注册者,(男女分别?)
(2)改写users.db.txt,记录在线用户
(3)虽然最好显示新刚注册者,但现为显示此时(或0点)注册者快照(snapshot),包括在线(刚才曾经在线)状态(和签名)
(4)打开一个main websocket,在未打开chat页时接收聊天邀请;(暂未)广播到其他用户窗口
(5)logout时做下面提到的注销动作
(6)关闭此页时:关闭此页websocket,应该不影响chat页面聊天

design_3.jpg
聊天(chat)页面:
(1)页面打开时打开一个master websocket
(2)在线用户区:即时增减
(3)点击send:
         (i)如果没有data websocket,新打开一个;
         (ii)peer标签或radio单选至少要有其一;(不能选自己作为聊天对象)
         (iii)沿着此websocket发送和接收聊天信息
(4)点击quit/exit:
         (i)关闭data websocket;
         (ii)清空peer(对方)标签;
         (iii)清空对话显示框;
         (iv)清空输入框;
(5)关闭此页:关闭master/data websocket;
(6)之后别的用户所在的peer标签应处理

login:

logout:
         (i)注销session
         (ii)关闭websocket
         (iii)在线用户减1
         (iv)vec[]中移除fd(server代码中)

关闭网页:关闭websocket

网页:
(1)新建/打开
(2)后退
(3)前进
(4)关闭
(5)刷新
(6)(链接打开与)地址栏输入URL打开




聊天测试需求:
(1)(main页回退到login页,再前进,要正确处理)
(2)一个人最多与6个人同时聊天
()
()        //TODO
()




编译运行:
       g++ -o test websocket_chat.cpp
      ./test 9001
(我测试服务器ip为192.168.64.132,所以下面1.php中建立websocket连接的代码为ws://192.168.64.132:9001)
websocket_chat.cpp:
 /*********************************************************************************************
*
*
  Copyright (c) 2020-2021  SGuniver_22 & thaiqi (shudan75@sina.com) 
  All Rights Reserved

  This program is free software; you can redistribute it and/or modify it
  under the terms of version 2 of the GNU General Public License as
  published by the Free Software Foundation.

  This program is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  more details.

  You should have received a copy of the GNU General Public License along with
  this program; if not, write to the Free Software Foundation, Inc., 59
  Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*
*
**********************************************************************************************/


#include 
#include 
#include 
#include 
#include 
#include 
#include  
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
using namespace std;


#define MAX_EVENT_NUMBER 1000

#define BUFFER_SIZE  20*1024*1024
int try_analyze_buffer(unsigned char* buffer, int* p_begin,  int in_amount, int sockfd);


//==================== 加密方法 sha1哈希 ====================

typedef struct SHA1Context
{
    uint32_t Message_Digest[5];
    uint32_t Length_Low;
    uint32_t Length_High;
    uint8_t Message_Block[64];
    int32_t Message_Block_Index;
    int32_t Computed;
    int32_t Corrupted;
} SHA1Context;

#define SHA1CircularShift(bits, word) ((((word) << (bits)) & 0xFFFFFFFF) | ((word) >> (32 - (bits))))

void SHA1ProcessMessageBlock(SHA1Context *context)
{
    const uint32_t K[] = {0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6};
    int32_t t;
    uint32_t temp;
    uint32_t W[80];
    uint32_t A, B, C, D, E;

    for (t = 0; t < 16; t++)
    {
        W[t] = ((uint32_t)context->Message_Block[t * 4]) << 24;
        W[t] |= ((uint32_t)context->Message_Block[t * 4 + 1]) << 16;
        W[t] |= ((uint32_t)context->Message_Block[t * 4 + 2]) << 8;
        W[t] |= ((uint32_t)context->Message_Block[t * 4 + 3]);
    }

    for (t = 16; t < 80; t++)
        W[t] = SHA1CircularShift(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]);

    A = context->Message_Digest[0];
    B = context->Message_Digest[1];
    C = context->Message_Digest[2];
    D = context->Message_Digest[3];
    E = context->Message_Digest[4];

    for (t = 0; t < 20; t++)
    {
        temp = SHA1CircularShift(5, A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
        temp &= 0xFFFFFFFF;
        E = D;
        D = C;
        C = SHA1CircularShift(30, B);
        B = A;
        A = temp;
    }
    for (t = 20; t < 40; t++)
    {
        temp = SHA1CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[1];
        temp &= 0xFFFFFFFF;
        E = D;
        D = C;
        C = SHA1CircularShift(30, B);
        B = A;
        A = temp;
    }
    for (t = 40; t < 60; t++)
    {
        temp = SHA1CircularShift(5, A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
        temp &= 0xFFFFFFFF;
        E = D;
        D = C;
        C = SHA1CircularShift(30, B);
        B = A;
        A = temp;
    }
    for (t = 60; t < 80; t++)
    {
        temp = SHA1CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[3];
        temp &= 0xFFFFFFFF;
        E = D;
        D = C;
        C = SHA1CircularShift(30, B);
        B = A;
        A = temp;
    }
    context->Message_Digest[0] = (context->Message_Digest[0] + A) & 0xFFFFFFFF;
    context->Message_Digest[1] = (context->Message_Digest[1] + B) & 0xFFFFFFFF;
    context->Message_Digest[2] = (context->Message_Digest[2] + C) & 0xFFFFFFFF;
    context->Message_Digest[3] = (context->Message_Digest[3] + D) & 0xFFFFFFFF;
    context->Message_Digest[4] = (context->Message_Digest[4] + E) & 0xFFFFFFFF;
    context->Message_Block_Index = 0;
}

void SHA1Reset(SHA1Context *context)
{
    context->Length_Low = 0;
    context->Length_High = 0;
    context->Message_Block_Index = 0;

    context->Message_Digest[0] = 0x67452301;
    context->Message_Digest[1] = 0xEFCDAB89;
    context->Message_Digest[2] = 0x98BADCFE;
    context->Message_Digest[3] = 0x10325476;
    context->Message_Digest[4] = 0xC3D2E1F0;

    context->Computed = 0;
    context->Corrupted = 0;
}

void SHA1PadMessage(SHA1Context *context)
{
    if (context->Message_Block_Index > 55)
    {
        context->Message_Block[context->Message_Block_Index++] = 0x80;
        while (context->Message_Block_Index < 64)
            context->Message_Block[context->Message_Block_Index++] = 0;
        SHA1ProcessMessageBlock(context);
        while (context->Message_Block_Index < 56)
            context->Message_Block[context->Message_Block_Index++] = 0;
    }
    else
    {
        context->Message_Block[context->Message_Block_Index++] = 0x80;
        while (context->Message_Block_Index < 56)
            context->Message_Block[context->Message_Block_Index++] = 0;
    }
    context->Message_Block[56] = (context->Length_High >> 24) & 0xFF;
    context->Message_Block[57] = (context->Length_High >> 16) & 0xFF;
    context->Message_Block[58] = (context->Length_High >> 8) & 0xFF;
    context->Message_Block[59] = (context->Length_High) & 0xFF;
    context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF;
    context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF;
    context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF;
    context->Message_Block[63] = (context->Length_Low) & 0xFF;

    SHA1ProcessMessageBlock(context);
}

int32_t SHA1Result(SHA1Context *context)
{
    if (context->Corrupted)
    {
        return 0;
    }
    if (!context->Computed)
    {
        SHA1PadMessage(context);
        context->Computed = 1;
    }
    return 1;
}

void SHA1Input(SHA1Context *context, const char *message_array, uint32_t length)
{
    if (!length)
        return;

    if (context->Computed || context->Corrupted)
    {
        context->Corrupted = 1;
        return;
    }

    while (length-- && !context->Corrupted)
    {
        context->Message_Block[context->Message_Block_Index++] = (*message_array & 0xFF);

        context->Length_Low += 8;

        context->Length_Low &= 0xFFFFFFFF;
        if (context->Length_Low == 0)
        {
            context->Length_High++;
            context->Length_High &= 0xFFFFFFFF;
            if (context->Length_High == 0)
                context->Corrupted = 1;
        }

        if (context->Message_Block_Index == 64)
        {
            SHA1ProcessMessageBlock(context);
        }
        message_array++;
    }
}

/* int32_t sha1_hash(const char *source, char *lrvar){//Main 
    SHA1Context sha; 
    char buf[128]; 
 
    SHA1Reset(&sha); 
    SHA1Input(&sha, source, strlen(source)); 
 
    if (!SHA1Result(&sha)){ 
        printf("SHA1 ERROR: Could not compute message digest"); 
        return -1; 
    } else { 
        memset(buf,0,sizeof(buf)); 
        sprintf(buf, "%08X%08X%08X%08X%08X", sha.Message_Digest[0],sha.Message_Digest[1], 
        sha.Message_Digest[2],sha.Message_Digest[3],sha.Message_Digest[4]); 
        //lr_save_string(buf, lrvar); 
         
        return strlen(buf); 
    } 
} */

char *sha1_hash(const char *source)
{
    SHA1Context sha;
    char *buf; //[128];

    SHA1Reset(&sha);
    SHA1Input(&sha, source, strlen(source));

    if (!SHA1Result(&sha))
    {
        printf("SHA1 ERROR: Could not compute message digest");
        return NULL;
    }
    else
    {
        buf = (char *)malloc(128);
        memset(buf, 0, 128);
        sprintf(buf, "%08X%08X%08X%08X%08X", sha.Message_Digest[0], sha.Message_Digest[1],
                sha.Message_Digest[2], sha.Message_Digest[3], sha.Message_Digest[4]);
        //lr_save_string(buf, lrvar);
        //return strlen(buf);
        return buf;
    }
}

int32_t tolower(int32_t c)
{
    if (c >= 'A' && c <= 'Z')
    {
        return c + 'a' - 'A';
    }
    else
    {
        return c;
    }
}

int32_t htoi(const char s[], int32_t start, int32_t len)
{
    int32_t i, j;
    int32_t n = 0;
    if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) //判断是否有前导0x或者0X
    {
        i = 2;
    }
    else
    {
        i = 0;
    }
    i += start;
    j = 0;
    for (; (s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'f') || (s[i] >= 'A' && s[i] <= 'F'); ++i)
    {
        if (j >= len)
        {
            break;
        }
        if (tolower(s[i]) > '9')
        {
            n = 16 * n + (10 + tolower(s[i]) - 'a');
        }
        else
        {
            n = 16 * n + (tolower(s[i]) - '0');
        }
        j++;
    }
    return n;
}
//==================== 加密方法BASE64 ====================

//base64编/解码用的基础字符集
const char ws_base64char[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

/*******************************************************************************
 * 名称: ws_base64_encode
 * 功能: ascii编码为base64格式
 * 参数: 
 *      bindata: ascii字符串输入
 *      base64: base64字符串输出
 *      binlength: bindata的长度
 * 返回: base64字符串长度
 * 说明: 无
 ******************************************************************************/
int32_t ws_base64_encode(const uint8_t *bindata, char *base64, int32_t binlength)
{
    int32_t i, j;
    uint8_t current;
    for (i = 0, j = 0; i < binlength; i += 3)
    {
        current = (bindata[i] >> 2);
        current &= (uint8_t)0x3F;
        base64[j++] = ws_base64char[(int32_t)current];
        current = ((uint8_t)(bindata[i] << 4)) & ((uint8_t)0x30);
        if (i + 1 >= binlength)
        {
            base64[j++] = ws_base64char[(int32_t)current];
            base64[j++] = '=';
            base64[j++] = '=';
            break;
        }
        current |= ((uint8_t)(bindata[i + 1] >> 4)) & ((uint8_t)0x0F);
        base64[j++] = ws_base64char[(int32_t)current];
        current = ((uint8_t)(bindata[i + 1] << 2)) & ((uint8_t)0x3C);
        if (i + 2 >= binlength)
        {
            base64[j++] = ws_base64char[(int32_t)current];
            base64[j++] = '=';
            break;
        }
        current |= ((uint8_t)(bindata[i + 2] >> 6)) & ((uint8_t)0x03);
        base64[j++] = ws_base64char[(int32_t)current];
        current = ((uint8_t)bindata[i + 2]) & ((uint8_t)0x3F);
        base64[j++] = ws_base64char[(int32_t)current];
    }
    base64[j] = '\0';
    return j;
}
/*******************************************************************************
 * 名称: ws_base64_decode
 * 功能: base64格式解码为ascii
 * 参数: 
 *      base64: base64字符串输入
 *      bindata: ascii字符串输出
 * 返回: 解码出来的ascii字符串长度
 * 说明: 无
 ******************************************************************************/
int32_t ws_base64_decode(const char *base64, uint8_t *bindata)
{
    int32_t i, j;
    uint8_t k;
    uint8_t temp[4];
    for (i = 0, j = 0; base64[i] != '\0'; i += 4)
    {
        memset(temp, 0xFF, sizeof(temp));
        for (k = 0; k < 64; k++)
        {
            if (ws_base64char[k] == base64[i])
                temp[0] = k;
        }
        for (k = 0; k < 64; k++)
        {
            if (ws_base64char[k] == base64[i + 1])
                temp[1] = k;
        }
        for (k = 0; k < 64; k++)
        {
            if (ws_base64char[k] == base64[i + 2])
                temp[2] = k;
        }
        for (k = 0; k < 64; k++)
        {
            if (ws_base64char[k] == base64[i + 3])
                temp[3] = k;
        }
        bindata[j++] = ((uint8_t)(((uint8_t)(temp[0] << 2)) & 0xFC)) |
                       ((uint8_t)((uint8_t)(temp[1] >> 4) & 0x03));
        if (base64[i + 2] == '=')
            break;
        bindata[j++] = ((uint8_t)(((uint8_t)(temp[1] << 4)) & 0xF0)) |
                       ((uint8_t)((uint8_t)(temp[2] >> 2) & 0x0F));
        if (base64[i + 3] == '=')
            break;
        bindata[j++] = ((uint8_t)(((uint8_t)(temp[2] << 6)) & 0xF0)) |
                       ((uint8_t)(temp[3] & 0x3F));
    }
    return j;
}

/*******************************************************************************
 * 名称: ws_buildRespondShakeKey
 * 功能: server端在接收client端的key后,构建回应用的key
 * 参数:
 *      acceptKey: 来自客户端的key字符串
 *      acceptKeyLen: 长度
 *      respondKey:  在 acceptKey 之后加上 GUID, 再sha1哈希, 再转成base64得到 respondKey
 * 返回: respondKey的长度(肯定比acceptKey要长)
 * 说明: 无
 ******************************************************************************/
int32_t ws_buildRespondShakeKey(char *acceptKey, uint32_t acceptKeyLen, char *respondKey)
{
    char *clientKey;
    char *sha1DataTemp;
    char *sha1Data;
    int32_t i, n;
    const char GUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    uint32_t GUIDLEN;

    if (acceptKey == NULL)
        return 0;
    GUIDLEN = sizeof(GUID);
    clientKey = (char *)calloc(acceptKeyLen + GUIDLEN + 10, sizeof(char));
    memset(clientKey, 0, (acceptKeyLen + GUIDLEN + 10));

    memcpy(clientKey, acceptKey, acceptKeyLen);
    memcpy(&clientKey[acceptKeyLen], GUID, GUIDLEN);
    clientKey[acceptKeyLen + GUIDLEN] = '\0';

    sha1DataTemp = sha1_hash(clientKey);
    n = strlen((const char *)sha1DataTemp);
    sha1Data = (char *)calloc(n / 2 + 1, sizeof(char));
    memset(sha1Data, 0, n / 2 + 1);

    for (i = 0; i < n; i += 2)
        sha1Data[i / 2] = htoi(sha1DataTemp, i, 2);
    n = ws_base64_encode((const uint8_t *)sha1Data, (char *)respondKey, (n / 2));

    free(sha1DataTemp);
    free(sha1Data);
    free(clientKey);
    return n;
}
/*******************************************************************************
 * 名称: ws_buildHttpRespond
 * 功能: 构建server端回复client连接请求的http协议
 * 参数:
 *      acceptKey: 来自client的握手key
 *      acceptKeyLen: 长度
 *      package: 存储
 * 返回: 无
 * 说明: 无
 ******************************************************************************/
void ws_buildHttpRespond(char *acceptKey, uint32_t acceptKeyLen, char *package)
{
    const char httpDemo[] =
        "HTTP/1.1 101 Switching Protocols\r\n"
        "Upgrade: websocket\r\n"
        "Server: Microsoft-HTTPAPI/2.0\r\n"
        "Connection: Upgrade\r\n"
        "Sec-WebSocket-Accept: %s\r\n"
        "%s\r\n\r\n"; //时间打包待续, 格式如 "Date: Tue, 20 Jun 2017 08:50:41 CST\r\n"
    time_t now;
    struct tm *tm_now;
    char timeStr[256] = {0};
    char respondShakeKey[256] = {0};
    //构建回应的握手key
    ws_buildRespondShakeKey(acceptKey, acceptKeyLen, respondShakeKey);
    //构建回应时间字符串
    time(&now);
    tm_now = localtime(&now);
    strftime(timeStr, sizeof(timeStr), "Date: %a, %d %b %Y %T %Z", tm_now);
    //组成回复信息
    sprintf(package, httpDemo, respondShakeKey, timeStr);
    //printf("package is %s\n", package);
}

/*******************************************************************************
 * 名称: ws_responseClient
 * 功能: 服务器回复客户端的连接请求, 以建立websocket连接
 * 参数:
 *      fd: 连接控制符
 *      data: 接收到来自客户端的数据(内含http连接请求)
 *      dataLen: 
 *      path: path匹配检查,不用可以置NULL
 * 返回: >0 建立websocket连接成功 <=0 建立websocket连接失败
 * 说明: 无
 ******************************************************************************/
int ws_responseClient(int fd, char *data,  char *path)
//int ws_responseClient(int fd, char *data, int dataLen, char *path)
{
    char *keyOffset;
    int32_t ret;
    char recvShakeKey[512] = {0};
    char respondPackage[1024] = {0};
    //path检查
    if (path && !strstr((char *)data, path))
        return -1;
    //获取握手key
    if (!(keyOffset = strstr((char *)data, "Sec-WebSocket-Key: ")))
        return -1;
    //获取握手key
    keyOffset += strlen("Sec-WebSocket-Key: ");
    sscanf((const char *)keyOffset, "%s", recvShakeKey);
    ret = strlen((const char *)recvShakeKey);
    if (ret < 1)
        return -1;
    //创建回复key
    ws_buildHttpRespond(recvShakeKey, (uint32_t)ret, respondPackage);
    //printf("response is %s\n", respondPackage);
    return send(fd, respondPackage, strlen((const char *)respondPackage), MSG_NOSIGNAL);
}


unsigned char buf[BUFFER_SIZE];
void et(struct epoll_event* events, int number, int epollfd, int listenfd)
{
	
}


int setnonblocking(int fd)
{
	int  old_option = fcntl(fd, F_GETFL);
	int  new_option = old_option | O_NONBLOCK;
	fcntl( fd, F_SETFL, new_option);
	return old_option;
	
}
void addfd(int epollfd, int fd, bool enable_et)
{
	struct epoll_event  event;
	event.data.fd = fd;
	event.events = EPOLLIN;
	if(enable_et)
        event.events  |= EPOLLET;
    epoll_ctl(epollfd,  EPOLL_CTL_ADD,  fd,  &event);
    setnonblocking( fd );
}

int main(int argc, char** argv)
{


   if( argc != 2)
   {
        printf("Usage: ./server_chat    port\n");
		return 1;
   }

   printf("abc's sha1 is %s\n", sha1_hash("abc"));



   const char*  ip = "180.76.157.1"; //argv[1];
   int port = atoi(argv[1]);
   
   int ret = 0;
   
   struct sockaddr_in  address;
	struct servent*   pse;
	struct protoent*  ppe;
   bzero(&address, sizeof(address));
   address.sin_family  =  AF_INET;
   address.sin_addr.s_addr =  INADDR_ANY;
   address.sin_port = htons(port);
		 ppe = getprotobyname("tcp") ;
   
   int listenfd = socket(PF_INET, SOCK_STREAM, ppe->p_proto);
   
   ret = bind(listenfd, (struct sockaddr* )&address, sizeof(address) );
   if(ret == -1)
   {
	   printf("bind error");
	   return -1;
   }
   
   ret = listen(listenfd, 5);
   if(ret == -1)
   {
	   printf("listen error");
	   return -1;
   }
   
   
   struct epoll_event events[MAX_EVENT_NUMBER];
   int  epollfd = epoll_create(5);
   if(epollfd == -1)
   {
	   printf("epoll_create error");
	   return -1;
   }
   addfd(epollfd, listenfd, true);
   
   while(1)
   {
	   int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
	   if(number < 0)
	   {
		   printf("epoll_wait failure\n");
		   break;
		   
	   }
	   //et(events, number, epollfd, listenfd);
	   
		for(int i = 0; i < number; i++)
		{
			int sockfd = events[i].data.fd;
			if(sockfd == listenfd)
			{
				struct sockaddr_in client_address;
				socklen_t  client_addrlength = sizeof(client_address);
				int connfd = accept(listenfd, (struct sockaddr*) &client_address, &client_addrlength);
				addfd(epollfd, connfd, true);
			}
			else if(events[i].events & EPOLLIN)
			{
				printf("EPOLLIN triggered once.\n");
				memset(buf, 0, BUFFER_SIZE);
				int begin  = 0;
				int amount = 0;
				while(1)
				{
RECV_DATA:
					int ret = recv(sockfd, buf+amount, BUFFER_SIZE-1, 0);
					if(ret < 0)
					{
						if(  errno==EAGAIN  ||  errno == EWOULDBLOCK )
						{
							printf("error EAGAIN or EWOULDBLOCK\n");
							break;
						}
						else
						{
							close(sockfd);
							break;
						}
					}
					else if(ret == 0)
					{
						close(sockfd);
					}
					else
					{
						printf("get %d bytes of content\n", ret);
						//printf("get %d bytes of content:%s\n", ret, buf);
						amount += ret;
						if(amount > BUFFER_SIZE)
						{
							printf("buffer maybe overflowed\n");
						
						    unsigned char tmpbuf[2];
						    tmpbuf[0] = 0x88;
						    tmpbuf[1] = 0x0;
						    int ret2 = send(sockfd, tmpbuf, 2, 0);
						    printf("send %d bytes.\n", ret2);
						
						}
					}
				}

                //analyze the packet
                if(amount > 0)
                {
					if(strstr((char*)buf, "HTTP"))
			    			ws_responseClient(sockfd, (char*)buf,  (char*)"/");
					else//buf amount
					{
                                          int state = 0;
                                          do{
                                          state = try_analyze_buffer(buf, &begin, amount, sockfd);
                                          if(state == -1)		 
							 goto RECV_DATA;
                                          }
                                          while(state == 1);
					}
					
					
				}					
			}
			else
			{
				printf("something else happened.\n");
			}
		}//for
	   
	   
   }
   
   close(listenfd);
   return 0;

}

typedef struct{
	string user_name;
	int    main_fd;
	
}main_fd_s;
vector  vec_main;

typedef struct{
	string user_name;
	int    master_fd;
	
}master_fd_s;
vector  vec_master;

typedef struct{
	string user_name;
	string peer_name;
	int    user_fd;
	int    peer_fd;
	//state
}user_fd_s;
vector vec_user;

/*
typedef struct{
	int    user_fd;
	int    peer_fd;
}fd_s;
vector vec_fd;
*/
void remove_main_fd_entry(int sockfd)
{
	int i;
	for(i = 0 ;  i< vec_main.size(); i++)
	{
		if(vec_main[i].main_fd == sockfd)
		{
			vec_main.erase(vec_main.begin()+i);
			i--;
		}
	}
	for(i=0; i < vec_main.size();i++)
		cout  << "main_fd:" << vec_main[i].user_name << vec_main[i].main_fd << endl;
	
}
void remove_main_fd_entry_2(char* name)
{
	int i;
	for(i = 0 ;  i< vec_main.size(); i++)
	{
		if(vec_main[i].user_name == name)
		{
			vec_main.erase(vec_main.begin()+i);
			i--;
		}
	}
	for(i=0; i < vec_main.size();i++)
		cout  << "main_fd:" << vec_main[i].user_name << vec_main[i].main_fd << endl;
	
}

void remove_master_fd_entry(int sockfd)
{
	int i;
	for(i = 0 ;  i< vec_master.size(); i++)
	{
		if(vec_master[i].master_fd == sockfd)
		{
			vec_master.erase(vec_master.begin()+i);
			i--;
		}
	}
	for(i=0; i < vec_master.size();i++)
		cout  << "master_fd:" << vec_master[i].user_name << vec_master[i].master_fd << endl;
	
}
void remove_master_fd_entry_2(char* name)
{
	int i;
	for(i = 0 ;  i< vec_master.size(); i++)
	{
		if(vec_master[i].user_name == name)
		{
			vec_master.erase(vec_master.begin()+i);
			i--;
		}
	}
	for(i=0; i < vec_master.size();i++)
		cout  << "master_fd:" << vec_master[i].user_name << vec_master[i].master_fd << endl;
	
}

void remove_user_fd_entry(int sockfd)
{
	int i;
	for(i = 0 ;  i< vec_user.size(); i++)
	{
		//FIXME:
		if(vec_user[i].user_fd == sockfd)
		{
			vec_user.erase(vec_user.begin()+i);
			i--;
		}
	}	
	for(i = 0 ;  i< vec_user.size(); i++)
	{
		//FIXME:
		if(vec_user[i].peer_fd == sockfd)
		{
			vec_user.erase(vec_user.begin()+i);
			i--;
		}
	}	
}

void remove_user_fd_entry_2(char* name)
{
	int i;
	for(i = 0 ;  i< vec_user.size(); i++)
	{
		//FIXME:
		if(vec_user[i].user_name == name)
		{
			vec_user.erase(vec_user.begin()+i);
			i--;
		}
	}	
	for(i = 0 ;  i< vec_user.size(); i++)
	{
		//FIXME:
		if(vec_user[i].peer_name == name)
		{
			vec_user.erase(vec_user.begin()+i);
			i--;
		}
	}	
}

void register_main_fd(char* name, int sockfd)
{
	main_fd_s t;
	t.user_name = name;
	t.main_fd = sockfd;
	vec_main.push_back(t);
	for(int i=0; i < vec_main.size();i++)
		cout  << "main_fd" << vec_main[i].user_name << vec_main[i].main_fd << endl;
}
void register_master_fd(char* name, int sockfd)
{
	master_fd_s t;
	t.user_name = name;
	t.master_fd = sockfd;
	vec_master.push_back(t);
	for(int i=0; i < vec_master.size();i++)
		cout  << "master_fd" << vec_master[i].user_name << vec_master[i].master_fd << endl;
}
void get_names(char* sender_name, char* recver_name, char* buf)
{
	int i;
	i =0;
	while(buf[i]!=' ')
	{
		if(buf[i]=='\n')
			break;
		sender_name[i] = buf[i];
		i++;
	}
	char* p = strstr(buf, "to ");
	p+=3;
	i = 0;
	while(*p != ':')
	{
		if(*p=='\n')
			break;
		recver_name[i++] = *p++;
	}
	
	printf("sender:%s, recver:%s\n", sender_name, recver_name); 
}

int find_fd(char* sender_name, char* recver_name, int sockfd)
{
	int i,j;
	for(i=0; i < vec_user.size();i++)
        cout  << "vec_user" << vec_user[i].user_name << vec_user[i].user_fd << vec_user[i].peer_name << vec_user[i].peer_fd << endl;
	for(i = 0 ;  i< vec_user.size(); i++)
	{
		if(vec_user[i].user_name == sender_name
		&& vec_user[i].peer_name == recver_name)
		    break;
	}
	if(i == vec_user.size())
	{
		user_fd_s    t,t2;
		t.user_name = sender_name;
		t.peer_name = recver_name;
		t.user_fd = sockfd;
		t.peer_fd = 0;
		vec_user.push_back(t);
		t2.user_name = recver_name;
		t2.peer_name = sender_name;
		t2.peer_fd = sockfd;
		t2.user_fd = 0;
		vec_user.push_back(t2);
		
		//return master_fd of peer_name
		for(j = 0; j < vec_master.size(); j++)
		{
			if(vec_master[j].user_name == recver_name)
				break;
		}
		if(j < vec_master.size())
		{
		    printf("1 return %d master_fd.\n",j );
		    return vec_master[j].master_fd;
		}
		else
		{
		    //return main_fd	
			for(j = 0; j < vec_main.size(); j++)
			{
				if(vec_main[j].user_name == recver_name)
					break;
			}
			if(j < vec_main.size())
			{
				printf("3 return %d main_fd.\n",j );
				return vec_main[j].main_fd;
			}
			else
			{
				printf("find fd error.\n");
				return -1;
			}
		}
		
	}
	else if(vec_user[i].peer_fd > 0)
	{
		if(vec_user[i].user_fd == 0)
			vec_user[i].user_fd = sockfd;
		
		for(j = 0; j < vec_user.size(); j++)
		{
			if(vec_user[j].user_name == recver_name
			&& vec_user[j].peer_name == sender_name)
				break;
		}
		if(j < vec_user.size())
		{
			//if(vec_user[i].user_fd == 0)
			//	vec_user[i].user_fd = sockfd;
			if(vec_user[j].peer_fd == 0)
				vec_user[j].peer_fd = sockfd;
		}
		
		printf("return user %d peer_fd.\n", i);
		return vec_user[i].peer_fd;
	}
	else if(vec_user[i].peer_fd == 0)
	{
		for(j = 0; j < vec_master.size(); j++)
		{
			if(vec_master[j].user_name == recver_name)
				break;
		}
		if(j < vec_master.size())
		{
			printf("2 return %d master_fd.\n",j );
			return vec_master[j].master_fd;
		}
		else
		{
		    //return main_fd	
			for(j = 0; j < vec_main.size(); j++)
			{
				if(vec_main[j].user_name == recver_name)
					break;
			}
			if(j < vec_main.size())
			{
				printf("4 return %d main_fd.\n",j );
				return vec_main[j].main_fd;
			}
			else
			{
				printf("find fd error.\n");
				return -1;
			}
		}
	}
}

int try_analyze_buffer(unsigned char* buffer, int* p_begin,  int in_amount, int sockfd)
{
                                                printf("looking at %p,begin:%d,amount:%d; \n", buffer, *p_begin, in_amount);

	                                        int amount = in_amount - *p_begin;
						if (amount < 2)
						{
							return -1;
						}
						
						unsigned char* buf = buffer + *p_begin;
						uint8_t FIN = buf[0] & 0x80;
						uint8_t OPCODE = buf[0] & 0x0F;
						uint8_t MASK = buf[1] & 0x80;
						uint8_t MASKS[4];
						uint32_t payload_len = buf[1] & 0x7F;
						unsigned long long pay_len = 0;
						printf("OPCODE:%d, MASK:%d, payload_len:%d\n", OPCODE,MASK,payload_len);
						int payload_begin = 0;
						long long len = 0;
						if(payload_len < 126)
						{
   						     len = 2+(MASK?4:0)+payload_len;
							 if( amount  < len)
								 return -1;
							 //pay_len = payload_len;
						    if(MASK){
						        MASKS[0] = buf[2];
						        MASKS[1] = buf[3];
						        MASKS[2] = buf[4];
						        MASKS[3] = buf[5];
						    }
						    payload_begin = 2+(MASK?4:0);
						    for(int i = 0,  j =0 ; i < payload_len; i++,j++)
						    {
							    j = j%4;
						        buf[payload_begin+i] ^= MASKS[j]; 
							if(OPCODE==1 || OPCODE ==0x2)
							{
							    printf("%d: %c, 0x%x\n", i, (char)buf[payload_begin+i], buf[payload_begin+i]);
							}
						    }
						    buf[payload_begin - 1] = payload_len;
						    buf[payload_begin - 2] = FIN | (OPCODE==0x9? 0xA:OPCODE);
							


						}
						else if(payload_len == 126)
						{
							 if(amount < 4)
								 return -1;
						     	
   						     pay_len = ( (uint32_t)(buf[2]<<8)+ buf[3] );
   						     len = 2+2+(MASK?4:0)+( (uint32_t)(buf[2]<<8)+ buf[3] );
							 if(amount < len)
								 return -1;
						    if(MASK){
						        MASKS[0] = buf[4];
						        MASKS[1] = buf[5];
						        MASKS[2] = buf[6];
						        MASKS[3] = buf[7];
						    }
						    payload_begin = 4+(MASK?4:0);
						    for(int i = 0,  j =0 ; i < pay_len; i++,j++)
						    {
							    j = j%4;
						        buf[payload_begin+i] ^= MASKS[j]; 
							if(OPCODE==1)
							{
							    printf("%d: %c, 0x%x\n", i, (char)buf[payload_begin+i], buf[payload_begin+i]);
							}
						    }
						    buf[payload_begin - 1] = pay_len & 0xFF;
						    buf[payload_begin - 2] = (pay_len & 0xFF00) >> 8;
						    buf[payload_begin - 3] = 126;
						    buf[payload_begin - 4] = FIN | OPCODE;
						}
						else if(payload_len == 127)
						{
							 if(amount < 10)
								 return -1;
   						     len = 2+8+(MASK?4:0);
							 len +=  ((unsigned long long)buf[2]<<56);
							 len +=  ((unsigned long long)buf[3]<<48);
							 len +=  ((unsigned long long)buf[4]<<40);
							 len +=  ((unsigned long long)buf[5]<<32);
							 len +=  ((unsigned long long)buf[6]<<24);
							 len +=  ((unsigned long long)buf[7]<<16);
							 len +=  ((unsigned long long)buf[8]<<8);
							 len +=  ((unsigned long long)buf[9]<<0);
							 if(amount < len)
								 return -1;
							 pay_len = len - 2-8 - (MASK?4:0);
						    if(MASK){
						        MASKS[0] = buf[10];
						        MASKS[1] = buf[11];
						        MASKS[2] = buf[12];
						        MASKS[3] = buf[13];
						    }
						    payload_begin = 10+(MASK?4:0);
						    for(int i = 0,  j =0 ; i < pay_len; i++,j++)
						    {
							    j = j%4;
						        buf[payload_begin+i] ^= MASKS[j]; 
							    if(OPCODE==1)
							    {
									if(i > pay_len - 16)
							            printf("%d: %c, 0x%x\n", i, (char)buf[payload_begin+i], buf[payload_begin+i]);
							    }
							}
						    
						    buf[payload_begin - 1] =  pay_len & 0xFF;
						    buf[payload_begin - 2] = (pay_len & 0xFF00) >> 8;
						    buf[payload_begin - 3] = (pay_len & 0xFF0000) >> 16;
						    buf[payload_begin - 4] = (pay_len & 0xFF000000) >> 24;
						    buf[payload_begin - 5] = (pay_len & 0xFF00000000) >> 32;
						    buf[payload_begin - 6] = (pay_len & 0xFF0000000000) >> 40;
						    buf[payload_begin - 7] = (pay_len & 0xFF000000000000) >> 48;
						    buf[payload_begin - 8] = (pay_len & 0xFF00000000000000) >> 56;
						    buf[payload_begin - 9] = 127;
						    buf[payload_begin - 10] = FIN | OPCODE;
							 
						}

						int ret2;
						if(OPCODE == 0x1 ||OPCODE == 0x0 || OPCODE == 0x2 || OPCODE == 0x9)
						{
                                                    int amount_out = 0;
                                                    int amount_sent = 0;
                                                    unsigned char* buf_out;


						    if(payload_len < 126)
						    {
						                                buf_out = buf+payload_begin -2;
                                                        amount_out = 2+payload_len;
						    }
							else if(payload_len == 126)
							{
						                                buf_out = buf+payload_begin -4;
                                                        amount_out = 4+pay_len;
							}
							else if(payload_len == 127)
							{
						                                buf_out = buf+payload_begin -10;
                                                        amount_out = 10+pay_len;
							}
							
							if(!strncmp((char*)buf+payload_begin,"@CTRL:I am ",11))
							{
								
								char name[126];
								memset(name, 0, sizeof(name));
								strncpy(name, (char*)buf+payload_begin+11, payload_len-11);
								printf("%s login1\n", name);
								register_main_fd(name, sockfd);
								
								//notice all other clients(?)
								
								return 0;
							}
							if(!strncmp((char*)buf+payload_begin,"I am ",5))
							{
								
								char name[126];
								memset(name, 0, sizeof(name));
								strncpy(name, (char*)buf+payload_begin+5, payload_len-5);
								printf("%s login2\n", name);
								register_master_fd(name, sockfd);
								
								//notice all other clients(?)
							    for(int i=0; i < vec_master.size()-1;i++)
								{
									printf("broadcasting to %d\n", vec_master[i].master_fd);
		                               ret2 = send(vec_master[i].master_fd , buf_out+amount_sent, amount_out-amount_sent, 0);
									   if(ret2 < amount_out)
										   printf("not sufficient sending\n");
								}

								
								
								return 0;
							}
							

							if(!strncmp((char*)buf+payload_begin,"@LOGOUT",7))
							{
								
								char name[126];
								memset(name, 0, sizeof(name));
								if(!strncmp((char*)buf+payload_begin,"@LOGOUT_MAIN:",13))
								{
								      strncpy(name, (char*)buf+payload_begin+13, payload_len-13);
								      printf("%s logout at 1\n", name);
									  
									remove_user_fd_entry_2(name);
									remove_master_fd_entry_2(name);
									remove_main_fd_entry_2(name);
								
									//notice all other clients(?) FIXME
									/*
									for(int i=0; i < vec_main.size();i++)
									{
										if(vec_main[i].user_name != name)
										{
										   printf("broadcasting logout 1 to %d\n", vec_main[i].main_fd);
										   ret2 = send(vec_main[i].main_fd , buf_out+amount_sent, amount_out-amount_sent, 0);
										   if(ret2 < amount_out)
											   printf("not sufficient sending when logout 1.\n");
										}
									}
									*/
									for(int i=0; i < vec_master.size();i++)
									{
										if(vec_master[i].user_name != name)
										{
										   printf("broadcasting logout 2 to %d\n", vec_master[i].master_fd);
										   ret2 = send(vec_master[i].master_fd , buf_out+amount_sent, amount_out-amount_sent, 0);
										   if(ret2 < amount_out)
											   printf("not sufficient sending when logout 2.\n");
										}
									}
								}
								else if(!strncmp((char*)buf+payload_begin,"@LOGOUT_MASTER:",15))
								{
								      strncpy(name, (char*)buf+payload_begin+15, payload_len-15);
								      printf("%s logout at 2\n", name);

									remove_user_fd_entry_2(name);
									remove_master_fd_entry_2(name);
									remove_main_fd_entry_2(name);
								
									//notice all other clients(?) FIXME
									/*
									for(int i=0; i < vec_main.size();i++)
									{
										if(vec_main[i].user_name != name)
										{
										   printf("broadcasting logout 1 to %d\n", vec_main[i].main_fd);
										   ret2 = send(vec_main[i].main_fd , buf_out+amount_sent, amount_out-amount_sent, 0);
										   if(ret2 < amount_out)
											   printf("not sufficient sending when logout 1.\n");
										}
									}
									*/
									for(int i=0; i < vec_master.size();i++)
									{
										if(vec_master[i].user_name != name)
										{
										   printf("broadcasting logout 2 to %d\n", vec_master[i].master_fd);
										   ret2 = send(vec_master[i].master_fd , buf_out+amount_sent, amount_out-amount_sent, 0);
										   if(ret2 < amount_out)
											   printf("not sufficient sending when logout 2.\n");
										}
									}
								}
								
								return 0;
							}


							char sender_name[126];
							char recver_name[126];
								memset(sender_name, 0, sizeof(sender_name));
								memset(recver_name, 0, sizeof(recver_name));
							get_names(sender_name, recver_name,(char*)buf+payload_begin);
							
							
								   //int fd = sockfd;
								   int fd = find_fd(sender_name, recver_name, sockfd);
								   printf("find_fd return %d\n", fd);

                                                        while(amount_sent < amount_out)
                                                        {
															printf("sent:%d, out:%d\n", amount_sent, amount_out);
						           //ret2 = send(sockfd, buf_out+amount_sent, amount_out-amount_sent, 0);
						           ret2 = send(fd, buf_out+amount_sent, amount_out-amount_sent, 0);
                                                           if(ret2>0)
                                                               amount_sent += ret2;
                                                           else if(ret2<0)
                                                               continue;
                                                           
                                                        }
						    printf("send %d/%d bytes.payload_begin:%d, payload_len:%d,pay_len:%lld.\n", amount_sent,amount_out, payload_begin, payload_len, pay_len);
                            fflush(stdout);
						}
						else if(OPCODE == 0x8)
						{
						    unsigned char tmpbuf[2];
						    tmpbuf[0] = 0x88;
						    tmpbuf[1] = 0x0;
							// sockfd ?
						    int ret2 = send(sockfd, tmpbuf, 2, 0);
							if(ret2 != 2)
						        printf("send %d/2 bytes error.\n", ret2);
							//TODO: remove fd from epoll()
							else
							{
								printf("websocket close connection.\n");
								close(sockfd);
								//TODO: remove fd from vec[]
								remove_user_fd_entry(sockfd);
								remove_master_fd_entry(sockfd);
								remove_main_fd_entry(sockfd);
							}
						}

                        printf("amount: %d, len: %lld\n", amount, len);
  					    if(amount == len)
						{
						    printf("a complete frame.\n");
						}
						else if(len > amount)
						{
						    printf("incorrect amount and len?\n");
						}
						else if(len < amount)
						{
						    printf("more frame?\n");
							*p_begin  += len;
                            //int state2 = try_analyze_buffer(buffer, p_begin, in_amount, sockfd);
			    //				return state2;
                                                    return 1;
						}
						return 0;
}

/*

         buf    *p_begin   amount
		 
		 
		           buf
		 
		 
		 
*/

 


测试用了selenium/webdriver和python 脚本:python test4.py
测试脚本:

test4.py:
 #!coding=utf-8
from selenium import webdriver
import time
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import UnexpectedAlertPresentException

chromedriver = "C:\Program Files (x86)\Google\Chrome\Application\chromedriver.exe"
driver = webdriver.Chrome(chromedriver)



driver.get("http://localhost/clear_users.php")

driver.get("http://localhost/-1.php")
window_1=driver.current_window_handle
js='window.open("http://localhost/-1.php")'
driver.execute_script(js)
window_2=driver.current_window_handle
js='window.open("http://localhost/-1.php")'
driver.execute_script(js)
window_3=driver.current_window_handle
js='window.open("http://localhost/-1.php")'
driver.execute_script(js)
window_4=driver.current_window_handle
js='window.open("http://localhost/-1.php")'
driver.execute_script(js)
window_5=driver.current_window_handle
js='window.open("http://localhost/-1.php")'
driver.execute_script(js)
window_6=driver.current_window_handle
js='window.open("http://localhost/-1.php")'
driver.execute_script(js)
window_7=driver.current_window_handle
js='window.open("http://localhost/-1.php")'
driver.execute_script(js)
window_8=driver.current_window_handle


#uu=0
#allhandles=driver.window_handles
#for handle in allhandles:
#    uu=uu+1
#    driver.switch_to.window(handle)
#    time.sleep(3)
#driver.switch_to.window(driver.window_handles[-1])
#time.sleep(1)



#############################################
#       
#   [0][7][6][5][4][3][2][1]
#
#
#
#
#
##

driver.switch_to.window(driver.window_handles[0])
driver.find_element_by_id("id_text1").send_keys("a")
time.sleep(1)
driver.find_element_by_id("id_submit1").click()
time.sleep(1)
driver.find_element_by_id("id_submit2").click()
time.sleep(1)

driver.switch_to.window(driver.window_handles[0])
time.sleep(1)

driver.close()



#driver.switch_to.window(driver.window_handles[0])
#time.sleep(1)

#result = EC.alert_is_present()(driver)
#if result:
#    print(result.text)
#    result.accept()
#else:
#    print("alert未弹出2")
    
#############################################
#       
#   [0][7][6][5][4][3][2][1]
#
#
#
#
#
##             ##############

time.sleep(1)
driver.switch_to.window(driver.window_handles[6])
driver.find_element_by_id("id_text1").send_keys("b")
time.sleep(1)
driver.find_element_by_id("id_submit1").click()
time.sleep(1)
driver.find_element_by_id("id_submit2").click()
time.sleep(1)

driver.switch_to.window(driver.window_handles[6])
time.sleep(1)
driver.close()


##             ##############

time.sleep(1)
driver.switch_to.window(driver.window_handles[5])
driver.find_element_by_id("id_text1").send_keys("c")
time.sleep(1)
driver.find_element_by_id("id_submit1").click()
time.sleep(1)
driver.find_element_by_id("id_submit2").click()
time.sleep(1)

driver.switch_to.window(driver.window_handles[5])
time.sleep(1)
driver.close()


##             ##############

time.sleep(1)
driver.switch_to.window(driver.window_handles[4])
driver.find_element_by_id("id_text1").send_keys("d")
time.sleep(1)
driver.find_element_by_id("id_submit1").click()
time.sleep(1)
driver.find_element_by_id("id_submit2").click()
time.sleep(1)

driver.switch_to.window(driver.window_handles[4])
time.sleep(1)
driver.close()


##             ##############

time.sleep(1)
driver.switch_to.window(driver.window_handles[3])
driver.find_element_by_id("id_text1").send_keys("e")
time.sleep(1)
driver.find_element_by_id("id_submit1").click()
time.sleep(1)
driver.find_element_by_id("id_submit2").click()
time.sleep(1)

driver.switch_to.window(driver.window_handles[3])
time.sleep(1)
driver.close()



##             ##############

time.sleep(1)
driver.switch_to.window(driver.window_handles[2])
driver.find_element_by_id("id_text1").send_keys("f")
time.sleep(1)
driver.find_element_by_id("id_submit1").click()
time.sleep(1)
driver.find_element_by_id("id_submit2").click()
time.sleep(1)

driver.switch_to.window(driver.window_handles[2])
time.sleep(1)
driver.close()


##             ##############

time.sleep(1)
driver.switch_to.window(driver.window_handles[1])
driver.find_element_by_id("id_text1").send_keys("g")
time.sleep(1)
driver.find_element_by_id("id_submit1").click()
time.sleep(1)
driver.find_element_by_id("id_submit2").click()
time.sleep(1)

driver.switch_to.window(driver.window_handles[1])
time.sleep(1)
driver.close()


##             ##############

time.sleep(1)
driver.switch_to.window(driver.window_handles[0])
driver.find_element_by_id("id_text1").send_keys("h")
time.sleep(1)
driver.find_element_by_id("id_submit1").click()
time.sleep(1)
driver.find_element_by_id("id_submit2").click()
time.sleep(1)

driver.switch_to.window(driver.window_handles[0])
time.sleep(1)
driver.close()


######################################################################################
driver.switch_to.window(driver.window_handles[-1])
time.sleep(1)

radio_user=driver.find_elements_by_name("radio_users")
radio_user[0].click()
time.sleep(2)
driver.find_element_by_id("id_text2").send_keys("hha1")
time.sleep(2)
driver.find_element_by_id("id_send_1").click()
time.sleep(2)

driver.switch_to.window(driver.window_handles[0])
time.sleep(2)
driver.switch_to.window(driver.window_handles[7])
time.sleep(2)



radio_user=driver.find_elements_by_name("radio_users")
radio_user[1].click()
time.sleep(2)
driver.find_element_by_id("id_text4").send_keys("hhb1")
time.sleep(2)
driver.find_element_by_id("id_send_2").click()
time.sleep(2)

driver.switch_to.window(driver.window_handles[1])
time.sleep(2)
driver.switch_to.window(driver.window_handles[7])
time.sleep(2)


    
radio_user=driver.find_elements_by_name("radio_users")
radio_user[2].click()
time.sleep(2)
driver.find_element_by_id("id_text6").send_keys("hhc1")
time.sleep(2)
driver.find_element_by_id("id_send_3").click()
time.sleep(2)

driver.switch_to.window(driver.window_handles[2])
time.sleep(2)
driver.switch_to.window(driver.window_handles[7])
time.sleep(2)
 



radio_user=driver.find_elements_by_name("radio_users")
radio_user[3].click()
time.sleep(2)
driver.find_element_by_id("id_text8").send_keys("hhd1")
time.sleep(2)
driver.find_element_by_id("id_send_4").click()
time.sleep(2)

driver.switch_to.window(driver.window_handles[3])
time.sleep(2)
driver.switch_to.window(driver.window_handles[7])
time.sleep(2)



radio_user=driver.find_elements_by_name("radio_users")
radio_user[4].click()
time.sleep(2)
driver.find_element_by_id("id_text10").send_keys("hhe1")
time.sleep(2)
driver.find_element_by_id("id_send_5").click()
time.sleep(2)

driver.switch_to.window(driver.window_handles[4])
time.sleep(2)
driver.switch_to.window(driver.window_handles[7])
time.sleep(2)
 


radio_user=driver.find_elements_by_name("radio_users")
radio_user[5].click()
time.sleep(2)
driver.find_element_by_id("id_text12").send_keys("hhf1")
time.sleep(2)
driver.find_element_by_id("id_send_6").click()
time.sleep(2)

driver.switch_to.window(driver.window_handles[5])
time.sleep(2)
driver.switch_to.window(driver.window_handles[7])
time.sleep(2)
 


radio_user=driver.find_elements_by_name("radio_users")
radio_user[6].click()                                         #should have reached max limit
time.sleep(2)
#driver.find_element_by_id("id_text14").send_keys("hhg1")     
time.sleep(2)
#driver.find_element_by_id("id_send_").click()                            
#time.sleep(2)

driver.switch_to.window(driver.window_handles[6])
time.sleep(2)
driver.switch_to.window(driver.window_handles[7])
time.sleep(2)
result = EC.alert_is_present()(driver)
if result:
    print(result.text)
    result.accept() 


                            ################################

driver.switch_to.window(driver.window_handles[0])
time.sleep(2)

radio_user=driver.find_elements_by_name("radio_users")
radio_user[0].click()                                         #should have reached max limit
time.sleep(2)
driver.find_element_by_id("id_text2").send_keys("aah1")     
time.sleep(2)
driver.find_element_by_id("id_send_1").click()              
time.sleep(2)


driver.switch_to.window(driver.window_handles[7])
time.sleep(2)


driver.switch_to.window(driver.window_handles[6])
time.sleep(2)

radio_user=driver.find_elements_by_name("radio_users")
radio_user[0].click()                                        
time.sleep(2)
driver.find_element_by_id("id_text2").send_keys("ggh1")     
time.sleep(2)
driver.find_element_by_id("id_send_1").click()               #h should have reached max limit
time.sleep(2)


driver.switch_to.window(driver.window_handles[7])
time.sleep(2)
result = EC.alert_is_present()(driver)
if result:
    print(result.text)
    result.accept() 
time.sleep(2)


driver.find_element_by_id("id_exit_2").click()             
time.sleep(2)
result = EC.alert_is_present()(driver)
if result:
    print(result.text)
    result.accept() 
time.sleep(2)


driver.switch_to.window(driver.window_handles[6])
time.sleep(2)
driver.find_element_by_id("id_send_1").click()               #h should now have idle
time.sleep(2)
driver.switch_to.window(driver.window_handles[7])
time.sleep(2)



driver.find_element_by_id("id_submit3").click()
time.sleep(2)
result = EC.alert_is_present()(driver)
while result:
    print(result.text)
    result.accept() 
    time.sleep(2)
    result = EC.alert_is_present()(driver)
    



#time.sleep(15)
#uu=0
#allhandles=driver.window_handles
#for handle in allhandles:
#    uu=uu+1
#    print(uu)
#    driver.switch_to.window(handle)
#    time.sleep(3)

 


效果如下面的GIF动画(用ScreenToGif录制,时长4分钟):



其中用到的主要的php文件:

1.php:




   
   
   
   
   
   
   
   







  用户
0) echo "".$name."
\n"; } fclose($fp); ?>








源码包在此:websocket_chat04_and_php02.zip 或者websocket_chat04_and_php03_and_css.zip

由于本人开发(特别是web开发)水平尚浅,希望更多的人可以加入进来或提出意见和建议,多多益善。

联系站长:13545246832(手机/微信)
电子邮箱:shudan75@sina.com

  

More powered by