Socket编程实现多人聊天室(c语言版)

socket编程花了我三四天的时间终于将这个程序给实现了!!!!

      所谓的多人聊天室,其实不过是客户端创建一个数据接收线程和数据发送线程,而在服务器端创建一个套接字数组,开启一个接受连接请求线程,不断接受来自客户端的连接请求,然后将建立的连接所形成的新套接字描述符存进套接字数组,并针对所存储的套接字描述符建立多个数据接收线程,对于所接收到的数据,开启一个数据转发进程,对套接字数组中的每个客户端将收到的数据进行转发。服务器就是起到这个一个数据转发的功能。

附上代码:

客户端:

 

    #include   
    #include   
    #include   
    #pragma comment(lib,"ws2_32.lib")  
    char buffer[4096] = {0};  
    int iRecvLen = 0;  
    int iSnedLen = 0;  
    char name[20];  
    void  THRE_RECV(SOCKET ClientSocket)  
    {  
        char buffer[50]={0};  
        while(1)  
        {  
            memset(buffer, 0, sizeof(buffer));///接收消息  
      
            iRecvLen = recv(ClientSocket, buffer, sizeof(buffer), 0);  
            if (SOCKET_ERROR == iRecvLen)  
            {  
                printf("send failed with error code: %d\n", WSAGetLastError());  
                closesocket(ClientSocket);  
                WSACleanup();  
                return -1;  
            }  
            ///printf("recv %d bytes from %s: ", iRecvLen, nameOther);//为了美观最好不要打印这个了  
            //strcat(buffer, "\0");  
            buffer[iRecvLen] = 0;  
            printf("\n%s\n", buffer);  
        }  
    }  
      
    int main()  
    {  
         WSADATA wsaData = { 0 };///存放套接字信息,WSADATA结构被用来保存AfxSocketInit函数返回的WindowsSockets初始化信息。  
        SOCKET ClientSocket = INVALID_SOCKET;///客户端套接字  
        ///printf("%d\n",INVALID_SOCKET);  
        SOCKADDR_IN ServerAddr = { 0 };///服务端地址  
        USHORT uPort = 18000;///服务端端口  
      
        ///初始化套接字  
        if(WSAStartup(MAKEWORD(2,2), &wsaData))///该函数的第一个参数指明程序请求使用的Socket版本,其中高位字节指明副版本、低位字节指明主版本  
        ///第二个参数返回请求的socket版本信息  
        {  
            printf("WSAStartup failed with error code: %d\n", WSAGetLastError());  
            return -1;  
        }  
        ///判断套接字版本  
        if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)  
        {  
            printf("wVersion was not 2.2\n");  
            return -1;  
        }  
        ///创建套接字  
        ClientSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);///AF_INET代表一个地址族,SOCK_STREAM表示为TCP协议的流服务,IPPROTO_TCP的值为6  
        ///printf("%d\n",IPPROTO_TCP);  
        if (ClientSocket == INVALID_SOCKET)  
        {  
            printf("socket初始化失败并返回错误代码: %d\n", WSAGetLastError());  
            return -1;  
        }  
        ///输入服务器IP  
        printf("Please input the server's IP:");  
        char IP[32] = { 0 };  
        gets(IP);  
        ///输入聊天的用户名  
        printf("Please input the Client's username:");  
        char name[32] = {0};  
        memset(name,0,sizeof(name));  
        gets(name);  
        ///设置服务器地址,填充服务器地址的结构  
        ServerAddr.sin_family = AF_INET;  
        ServerAddr.sin_port = htons(uPort);///服务器端口  
        ServerAddr.sin_addr.S_un.S_addr = inet_addr(IP);///服务器地址  
      
        printf("Connecting...........\n");  
        ///连接服务器  
        if(SOCKET_ERROR == connect(ClientSocket, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)))  
        {  
            printf("Connect failed with error code: %d \n", WSAGetLastError());  
            closesocket(ClientSocket);  
            WSACleanup();  
            return -1;  
        }  
        printf("Successfuuly got a connection from IP:%s Port:%d\n\n\n\n",inet_ntoa(ServerAddr.sin_addr),htons(ServerAddr.sin_port));  
        _beginthreadex(NULL,0,&THRE_RECV,ClientSocket,NULL,0);  
        iSnedLen = send(ClientSocket,name,strlen(name),0);  
        if(SOCKET_ERROR == iSnedLen)  
        {  
            printf("send failed with error code: %d\n", WSAGetLastError());  
            closesocket(ClientSocket);  
            WSACleanup();  
            return -1;  
        }  
        while(1)  
        {  
            memset(buffer, 0, sizeof(buffer));  
            ///发送消息  
      
            gets(buffer);  
            if(strcmp(buffer,"bye") == 0) break;  
            printf("%s: ", name);  
            iSnedLen = send(ClientSocket, buffer, strlen(buffer), 0);  
            if (SOCKET_ERROR == iSnedLen)  
            {  
                printf("send failed with error code: %d\n", WSAGetLastError());  
                closesocket(ClientSocket);  
                WSACleanup();  
                return -1;  
            }  
        }  
        closesocket(ClientSocket);  
        WSACleanup();  
        system("pause");  
        return 0;  
    }  

服务器端:

    #include   
    #include   
    #include   
    #include   
    #include   
    #pragma comment(lib,"ws2_32.lib")  
      
    #define SEND_OVER 1                          ///已经转发消息  
    #define SEND_YET  0                          ///还没转发消息  
      
    typedef struct _Client  
    {  
        SOCKET sClient;      ///客户端套接字  
        char buf[128];       ///数据缓冲区  
        char userName[16];   ///客户端用户名  
        char IP[20];         ///客户端IP  
        UINT_PTR flag;       ///标记客户端,用来区分不同的客户端  
        int first;  
    }Client;  
      
    Client g_Client[10] = { 0 };  
      
    int  SENDFFFF = 0;  
    int  Rflag = 0;  
      
    SOCKADDR_IN ServerAddr = { 0 };///服务端地址  
    SOCKADDR_IN ClientAddr = { 0 };///客户端地址  
    int iClientAddrLen = sizeof(ClientAddr);  
    SOCKET g_ServerSocket = INVALID_SOCKET;      ///服务端套接字  
    SOCKADDR_IN g_ClientAddr = { 0 };            ///客户端地址  
    int g_iClientAddrLen = sizeof(g_ClientAddr);  
    USHORT uPort = 18000;                       ///服务器监听端口  
    unsigned __stdcall ThreadSend(void* param)  
    {  
        while(1)  
        {  
            if(SENDFFFF==0) continue;  
            else  
            {  
                SOCKET client = INVALID_SOCKET;                 ///创建一个临时套接字来存放要转发的客户端套接字  
                char temp[128];  
                memset(temp,0,sizeof(temp));                        ///创建一个临时的数据缓冲区,用来存放接收到的数据  
                memcpy(temp, g_Client[Rflag].buf, sizeof(temp));  
                printf("temp = %s\n",temp);  
                int k = 1;  
                while(k<=5)  
                {  
                    if(k!=Rflag)  
                    {  
                         sprintf(g_Client[k].buf, "%s: %s", g_Client[Rflag].userName, temp);///添加一个用户名头  
                    }  
                    k++;  
                }  
                k=1;  
                while(k<=5)  
                {  
                    if(strlen(temp) != 0&&g_Client[k].sClient!=INVALID_SOCKET&&k!=Rflag)  
                    {  
                        int ret = send(g_Client[k].sClient, g_Client[k].buf, strlen(g_Client[k].buf), 0);  
                        printf("g_Client[k].buf == .............%s\n",g_Client[k].buf);  
                        if(ret==SOCKET_ERROR) return 1;  
                    }  
                    k++;  
                }  
                Rflag = 0;  
                SENDFFFF = 0;  
            }  
        }  
      
        return 0;  
    }  
      
    unsigned __stdcall THREAD_RECV(SOCKET ClientSocket)  
    {  
        int flag = 0;  
        int u = 1;  
        while(u<=5)  
        {  
            if(g_Client[u].sClient==ClientSocket&&g_Client!=INVALID_SOCKET)  
            {  
                flag = u;  
            }  
          u++;  
        }  
        printf("flag = %d\n",flag);  
        char temp[80] = {0};  
        while(1)  
        {  
            memset(temp, 0, sizeof(temp));  
            int ret = recv(ClientSocket, temp, sizeof(temp), 0); ///接收数据  
            printf("%s\n",temp);  
            if (ret == SOCKET_ERROR)  
            {  
                    printf("ClientSocket=%d  error=%d",ClientSocket,errno);  
                    //getchar();  
                    continue;  
            }  
      
                //iStatus = SEND_YET;                                ///设置转发状态为未转发  
            //flag = client == g_Client[0].sClient ? 1 : 0;        ///这个要设置,否则会出现自己给自己发消息的BUG  
            int k = 1;  
            while(k<=5)  
            {  
                if(k!=flag&&g_Client[flag].first==1)  
                    memcpy(g_Client[k].buf, temp, strlen(g_Client[k].buf));  
                printf("%s\n",g_Client[k].buf);  
                k++;  
            }  
            if(g_Client[flag].first==0)///如果这是第一次传过来的数,说明这是一个用户名,不需要进行转发,将其复制到该相应的用户名当中  
            {  
                memcpy(g_Client[flag].userName,temp,sizeof(temp));  
                g_Client[flag].first = 1;  
            }  
            else  
            {  
                Rflag = flag;  
                SENDFFFF = 1; ///开启一个转发线程,flag标记着要转发给哪个客户端  
            }  
        }  
    }  
    unsigned __stdcall THREAD_ACCEPT(SOCKET ClientSocket)  
    {  
      
        int num = 0;  
        while(1)  
        {  
            num = 0;  
            int  k = 1;  
           while(k<=5)  
           {  
            if(g_Client[k].sClient!=INVALID_SOCKET)  
            {  
                num++;  
                k++;  
                continue;  
            }  
            else  
            {  
                g_Client[k].sClient = accept(ClientSocket, (SOCKADDR*)&g_ClientAddr, &g_iClientAddrLen);  
                if(g_Client[k].sClient == INVALID_SOCKET)  
                {  
                    printf("accept failed with error code: %d\n", WSAGetLastError());  
                    closesocket(ClientSocket);  
                    WSACleanup();  
                    return -1;  
                }  
                //int len = recv(g_Client[k].sClient, g_Client[k].userName, 15, 0); ///接收用户名  
                //g_Client[k].userName[len]=0;  
                //printf("\nlen = %d\n",len);  
                printf("Successfuuly got a connection from IP:%s ,Port: %d,UerName: %s\n",inet_ntoa(g_ClientAddr.sin_addr), htons(g_ClientAddr.sin_port), g_Client[k].userName);  
                //printf("%s\n",g_Client[k].sClient);  
                printf("k = %d\n",k);  
                memcpy(g_Client[k].IP, inet_ntoa(g_ClientAddr.sin_addr), sizeof(g_Client[k].IP)); ///记录客户端IP  
                g_Client[k].flag = g_Client[k].sClient; ///不同的socke有不同UINT_PTR类型的数字来标识  
                num++;  
                k++;  
                break;  
            }  
          }  
          //printf("num = %d\n",num);  
          if(num>=3)  
          {  
              int l =1;  
              while(l<=5)  
              {  
                  if(g_Client[l].sClient!=INVALID_SOCKET)  
                  {  
                      _beginthreadex(NULL,0,&THREAD_RECV,g_Client[l].sClient,0,0);  
                  }  
                  l++;  
              }  
          }  
      
      
        }  
    }  
      
      
    int main()  
    {  
        ///存放套接字信息的结构  
        WSADATA wsaData = { 0 };  
        SOCKET ServerSocket = INVALID_SOCKET;///服务端套接字  
        SOCKET ClientSocket = INVALID_SOCKET;///客户端套接字  
        int pp = 1;  
        while(pp<=6)  
        {  
            memset(g_Client[pp].buf,0,sizeof(g_Client[pp].buf));  
            g_Client[pp].first = 0;  
            memset(g_Client[pp].IP,0,sizeof(g_Client[pp].IP));  
            g_Client[pp].sClient = INVALID_SOCKET;  
            memset(g_Client[pp].userName,0,sizeof(g_Client[pp].userName));  
            pp++;  
        }  
      
        if (WSAStartup(MAKEWORD(2, 2), &wsaData))  
        {  
            printf("WSAStartup failed with error code: %d\n", WSAGetLastError());  
            return -1;  
        }  
        ///判断版本  
        if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)  
        {  
            printf("wVersion was not 2.2\n");  
            return -1;  
        }  
        ///创建套接字  
        ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
        if (ServerSocket == INVALID_SOCKET)  
        {  
            printf("socket failed with error code: %d\n", WSAGetLastError());  
            return -1;  
        }  
      
        ///设置服务器地址  
        ServerAddr.sin_family = AF_INET;///连接方式  
        ServerAddr.sin_port = htons(uPort);///服务器监听端口  
        ServerAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);///任何客户端都能连接这个服务器  
        ///初始化套接字  
                                                            ///绑定服务器  
        if (SOCKET_ERROR == bind(ServerSocket, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)))  
        {  
            printf("bind failed with error code: %d\n", WSAGetLastError());  
            closesocket(ServerSocket);  
            return -1;  
        }  
        ///监听有无客户端连接  
        if (SOCKET_ERROR == listen(ServerSocket, 1))  
        {  
            printf("listen failed with error code: %d\n", WSAGetLastError());  
            closesocket(ServerSocket);  
            WSACleanup();  
            return -1;  
        }  
         _beginthreadex(NULL, 0, ThreadSend, NULL,0,NULL);  
         _beginthreadex(NULL,0,&THREAD_ACCEPT,ServerSocket,NULL,0);  
         int k = 0;  
        while(k<100)///让主线程休眠,不让它关闭TCP连接.  
        {  
             Sleep(10000000);  
             k++;  
        }  
      
        ///关闭套接字  
        int j = 1;  
        while(j<6)  
        {  
            if (g_Client[j].sClient != INVALID_SOCKET)  
                closesocket(g_Client[j].sClient);  
            j++;  
        }  
        closesocket(g_ServerSocket);  
        WSACleanup();  
        return 0;  
        /* 
        ClientSocket = accept(ServerSocket, (SOCKADDR*)&ClientAddr, &iClientAddrLen);///accept为阻塞函数 
        if (ClientSocket == INVALID_SOCKET) 
        { 
            printf("accept failed with error code: %d\n", WSAGetLastError()); 
            closesocket(ServerSocket); 
            WSACleanup(); 
            return -1; 
        } 
        printf("Successfuuly got a connection from IP:%s Port:%d\n\n\n\n",inet_ntoa(ClientAddr.sin_addr),htons(ClientAddr.sin_port)); 
     
    */  
      
      
      
    }  

 

对于代码的思路和注释都写在代码里面了,希望这个文档对于刚学习网络编程的人有所帮助。

 不过这个程序有一个问题就是在客户端断开连接的时候会出现错误,还有待解决!!可以对每个客户端的连接建立一个线程进行单独的循环查询,当检测到其断开时对其进行关闭套接字,关闭相应的线程。

你可能感兴趣的:(Socket编程实现多人聊天室(c语言版))