`

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

 
阅读更多

       socket编程花了我三四天的事件终于将这个程序给实现了大笑大笑

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

附上代码

客户端:

#include <stdio.h>
#include <winsock2.h>
#include <pthread.h>
#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 <WinSock2.h>
#include <process.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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));

*/



}

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

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

 

分享到:
评论

相关推荐

    windows环境下C语言多线程实现网络编程,多人聊天室,[总结].pdf

    Windows 环境下 C 语言多线程实现网络编程多人聊天室总结 在 Windows 环境下实现多人聊天室需要使用 C 语言和多线程技术来实现网络编程。下面是关于这个主题的知识点总结: 第一部分:Windows Socket 编程 * 使用...

    socket 实现多人聊天

    总的来说,实现“socket多人聊天”涉及到的知识点包括TCP/IP协议基础、Socket编程、线程处理、数据传输以及错误处理等。通过理解和掌握这些技术,我们可以构建出一个高效、可靠的多人聊天系统。在提供的压缩包文件中...

    操作系统与Linux程序设计团队作业,基于Socket编程的多人聊天室 <C语言实现>+源代码+文档说明

    操作系统与Linux程序设计团队作业,基于Socket编程的多人聊天室 &lt;C语言实现&gt;+源代码+文档说明 - 小白不懂运行,下载完可以私聊问,可远程教学 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才...

    java的Socket实现的多人聊天程序

    总的来说,这个Java的Socket实现的多人聊天程序是一个很好的学习示例,涵盖了网络编程、多线程、并发控制以及用户界面设计等多个方面的知识。通过分析和研究这个项目,开发者可以深入理解Java的Socket通信机制,为...

    基于socket的简易多人聊天室

    基于Socket的简易多人聊天室是利用这一技术实现的一个多用户交互的平台。在这个聊天室中,用户可以通过发送消息与房间内的其他用户实时交流。 【描述】: "基于socket的简易多人聊天室基于socket的简易多人聊天室...

    java实现socket编程网络通信多人聊天室

    通过以上步骤,一个简单的Java Socket编程实现的多人聊天室就搭建完成了。这样的系统适合初学者用来学习网络通信的基础知识,包括TCP连接、多线程编程、数据的序列化与反序列化等。通过实际操作,可以帮助理解网络...

    【Java socket编程】多人聊天室 源代码

    总结来说,Java Socket编程在实现多人聊天室这样的应用场景中,通过创建ServerSocket监听客户端连接,使用Socket进行数据交换,配合多线程技术处理并发通信,以及合理的数据传输格式和错误处理策略,可以构建出稳定...

    Python实现的多人聊天室源码,基于socket tcp通信,使用tkinter做客户端界面,含可执行文件

    在本项目中,我们探讨的是一个使用Python编程语言实现的多人聊天室,它基于socket TCP通信协议,并且利用Tkinter库构建了用户友好的客户端界面。这个聊天室系统允许多个用户同时在线交流,增强了实时互动性。以下是...

    C语言基于socket多人聊天(包含注册登录)

    在本项目中,"C语言基于socket多人聊天(包含注册登录)"是一个使用C语言开发的网络应用程序,它利用了socket编程来实现一个简单的聊天室功能。这个程序具有用户注册和登录的功能,只有验证通过的用户才能发送信息。...

    socket 简易多人聊天室

    在多人聊天室场景中,服务端需要管理多个客户端连接,这通常通过多线程实现。每个连接到服务器的客户端都会启动一个新的线程来处理其发送和接收的数据,这样可以确保服务端能同时处理多个客户端的并发请求,避免阻塞...

    Java Socket多人聊天室

    Java Socket多人聊天室是一种基于Java Socket编程实现的网络通信应用,它允许多个用户通过网络连接进行实时交流。在这个系统中,Socket充当了客户端与服务器之间的通信桥梁,实现了数据的双向传输,即用户可以发送...

    实验三socket编程代码.rar_socket编程_tcp/udp_一对多聊天_多人聊天室_计算机网络实验

    我们将深入理解TCP和UDP这两种传输层协议,并通过具体的代码实现一对多聊天和多人聊天室功能。 首先,TCP(Transmission Control Protocol)是一种面向连接、可靠的协议,它确保了数据的顺序传输和错误检查。在TCP ...

    java socket多线程多人聊天室

    总的来说,这个项目展示了Java Socket通信和多线程技术在实现多人聊天室中的应用。开发者需要理解网络编程的基本原理,熟悉Java的并发控制,以及如何设计和实现一个健壮的网络应用程序。通过这个项目,学习者不仅...

    多人聊天室源码.rar

    【标题】:“多人聊天室源码.rar”是一个包含Java编程语言实现的多人在线聊天室的源代码压缩包。这个项目可能是一个实时通信系统的实例,旨在教授如何构建一个基本的网络聊天平台,允许用户进行实时交流。 【描述】...

    用Java Socket实现的一个多人聊天的小软件

    本项目“用Java Socket实现的一个多人聊天的小软件”就是一个很好的实践案例,它利用了Java的Socket和Thread类,创建了一个简单的多用户聊天室。下面将详细解释这个项目中的关键知识点。 首先,`Socket`在Java中...

    socket多人聊天室代码+详细解释

    【标题】"socket多人聊天室代码+详细解释"是一个基于计算机网络技术的项目,主要使用了socket编程来实现一个能够支持多人实时交流的聊天室。这个项目特别适合于计算机网络实验,让学生深入理解网络通信的基本原理和...

    java编程实现多人聊天室功能

    在Java中,实现多人聊天室功能需要使用Socket编程来实现客户端和服务器之间的通信。我们可以使用Java的Socket类来创建一个套接字,用于与服务器建立连接。 Java多人聊天室实现步骤 1. 创建服务器套接字:使用...

    socket+gui实现多人聊天室

    本项目标题为“socket+GUI实现多人聊天室”,这意味着我们将探讨如何利用socket编程技术和GUI库来创建一个支持多用户实时交流的平台。 首先,让我们了解核心组件——socket。Socket是网络通信的基础,它提供了进程...

    《Java程序设计实训》报告 多人聊天室

    《Java程序设计实训》报告的主题是构建一个多人聊天室,旨在通过这个项目加深对Java语言的理解,特别是Java的多线程、GUI编程以及Socket网络编程技术。以下是对这些知识点的详细说明: 1. **Java语言**:Java是一种...

    多人聊天室python实现

    【标题】"多人聊天室python实现"涉及到的核心技术是使用Python编程语言构建一个支持多用户交互的聊天系统。在Python中,这样的应用通常基于网络通信协议TCP(Transmission Control Protocol)来建立连接,允许多个...

Global site tag (gtag.js) - Google Analytics