C/C++下TCP, 服务器/客户端 "多人聊天室"

原文地址:https://www.cnblogs.com/imwtr/p/4072452.html

本次实验利用TCP/IP, 语言环境为 C/C++

利用套接字Socket编程,以及线程处理,

实现Server/CLient 之间多人的聊天系统的基本功能。

结果大致如:

 

 

下面贴上代码(参考参考...)

Server 部分:

 

/* TCPdtd.cpp - main, TCPdaytimed */

#include <stdlib.h>
#include <stdio.h>
#include <winsock2.h>
#include <time.h>
#include "conio.h"
#include <windows.h>
#include <process.h>
#include <math.h>

#define QLEN       5
#define    WSVERS    MAKEWORD(2, 0)
#define    BUFLEN    2000             // 缓冲区大小
#pragma comment(lib,"ws2_32.lib")  //winsock 2.2 library

    SOCKET    msock, ssock;            /* master & slave sockets           */
    SOCKET    sockets[100] = {NULL};
     
     int cc;
     char    *pts;                    /* pointer to time string           */
     time_t    now;                    /* current time                       */
     char buf[2000];                      /* buffer                          */
     char *input;
     HANDLE hThread1,hThread[100] = {NULL};
    unsigned int threadID,ThreadID[100],number;

    struct    sockaddr_in fsin;
    struct    sockaddr_in Sin;

unsigned int __stdcall Chat(PVOID PM) 
{    
        char buf1[2000];
        char buf2[2000];
        char buf3[2000];
        char buf4[2000];
        (void) time(&now);
        pts = ctime(&now);
        sockets[number] = ssock;
        SOCKET    sock = ssock;
        ThreadID[number] = threadID;
        unsigned int threadid = threadID;
        sprintf(buf1," 时间: %s  \t【我的线程号: %d 】\n",pts,threadid);
        (void) send(sock,buf1, sizeof(buf1), 0); 
        sprintf(buf2," 线程号 <%d> 客户<IP:%s 端口:%d>  enter  \n",threadid,inet_ntoa(fsin.sin_addr),fsin.sin_port);
        printf("%s ",buf2);        
        printf("\t将自动把此数据发送给所有客户! \n");
        for(int i=0;i<=number;i++)
        {            
            if(sockets[i] != NULL && sockets[i] != sock)
            {
            (void) send(sockets[i],buf2, sizeof(buf2), 0); 
            printf(" 发送至线程号<%d>成功!\n",ThreadID[i]);
            }
        }
        printf(" \n");


flag1:cc = recv(sock, buf3, BUFLEN, 0);   //cc为接收的字符数
    if(cc == SOCKET_ERROR|| cc == 0)
    {
        (void) time(&now);
        pts = ctime(&now);
        sprintf( buf3," 线程号 <%d> 客户<IP:%s 端口:%d>  leave !  \n \t\t时间: %s",threadid,inet_ntoa(fsin.sin_addr),fsin.sin_port,pts);
        sock = NULL;    
        sockets[number] = NULL;
        CloseHandle(hThread[number]);
        printf("%s ", buf3);        
        printf("\t将自动把此数据发送给所有客户! \n");
        for(int i=0;i<=number;i++)
        {            
            if(sockets[i] != NULL && sockets[i] != sock)
            {
            (void) send(sockets[i], buf3, sizeof(buf3), 0);     
            printf(" 发送至线程号<%d>成功!\n",ThreadID[i]);
            }            
        }
    printf(" \n");
    }

    else if(cc > 0) 
    {
        (void) time(&now);
        pts = ctime(&now);
    sprintf(buf4," 线程号 <%d> 客户<IP:%s 端口:%d>说 :%s  \n \t\t时间 : %s",threadid,inet_ntoa(fsin.sin_addr),fsin.sin_port,buf3,pts);
    
        printf("%s ",buf4);
        printf("\t将自动把此数据发送给所有客户! \n");
        for(int i=0;i<=number;i++)
        {            
            if(sockets[i] != NULL && sockets[i] != sock)
            {
            (void) send(sockets[i],buf4, sizeof(buf4), 0);         
            printf(" 发送至线程号<%d>成功!\n",ThreadID[i]);
            }            
        }
        printf(" \n");

        goto flag1;
    }
        (void) closesocket(sock);
    
        return 0;
        }


/*------------------------------------------------------------------------
 * main - Iterative TCP server for DAYTIME service
 *------------------------------------------------------------------------
 */
void main(int argc, char *argv[]) 
/* argc: 命令行参数个数, 例如:C:\> TCPdaytimed 8080 
                     argc=2 argv[0]="TCPdaytimed",argv[1]="8080" */
{
    int     alen;                    /* from-address length               */    
    WSADATA wsadata; 
    char    *service = "5050";    
    WSAStartup(WSVERS, &wsadata);                         //加载 winsock 2.2 library
    msock = socket(PF_INET, SOCK_STREAM, 0);              //生成套接字。TCP协议号=6, UDP协议号=17
    memset(&Sin, 0, sizeof(Sin));
    Sin.sin_family = AF_INET;
    Sin.sin_addr.s_addr = INADDR_ANY;                    //指定绑定接口的IP地址。INADDR_ANY表示绑定(监听)所有的接口。
    Sin.sin_port = htons((u_short)atoi(service));        //atoi--把ascii转化为int,htons - 主机序(host)转化为网络序(network), s(short) 
    bind(msock, (struct sockaddr *)&Sin, sizeof(Sin));   // 绑定端口号(和IP地址)
    listen(msock, 5);                                    //队列长度为5

    printf("\t\t\t\t Chat 多人聊天程序 \n");
    printf("\t\t\t\t       (Server) \n");
     (void) time(&now);
      pts = ctime(&now);
    printf("\t\t\t  时间 :%s",pts);
        number = -1;
    while(1)                                    //检测是否有按键
    {                
        alen = sizeof(struct sockaddr);
        ssock = accept(msock, (struct sockaddr *)&fsin, &alen);
        number ++;
        hThread[number] = (HANDLE)_beginthreadex(NULL, 0,Chat,NULL, 0, &threadID);        
    }
    (void) closesocket(msock);
    WSACleanup();                         //卸载载 winsock 2.2 library
}

Client 部分:

/* TCPClient.cpp  -- 用于传递struct */
#include <stdlib.h>
#include <stdio.h>
#include <winsock2.h>
#include <string.h>
#include <time.h>
#include <windows.h>
#include <process.h>
#include <math.h>

#define    BUFLEN        2000                  // 缓冲区大小
#define WSVERS        MAKEWORD(2, 0)        // 指明版本2.0 
#pragma comment(lib,"ws2_32.lib")         // 指明winsock 2.0 Llibrary

/*------------------------------------------------------------------------
 * main - TCP client for DAYTIME service
 *------------------------------------------------------------------------
 */
    
    SOCKET    sock,sockets[100] = {NULL};                          /* socket descriptor            */
//    int    cc;                                /* recv character count            */
    char    *packet = NULL;               /* buffer for one line of text    */
    char *pts,*input;
    HANDLE hThread;
    unsigned threadID;

unsigned int __stdcall Chat(PVOID PM ) 
{
       time_t    now;
      (void) time(&now);
       pts = ctime(&now);
       char buf[2000];

    while(1)
    {
        int cc = recv(sock, buf, BUFLEN, 0);   //cc为接收的字符数
        if(cc == SOCKET_ERROR|| cc == 0)
        {
            printf("Error: %d.----",GetLastError());
            printf("与服务器断开连接!\n");
            CloseHandle(hThread);
            (void)closesocket(sock);
            break;
        }
        else if(cc > 0) 
        {
        //    buf[cc] = '\0';
            printf("%s\n",buf);
        //    printf("输入数据(exit退出):  \n");
        }     
    }
    return 0;
}

int main(int argc, char *argv[])
{
    time_t    now;
     (void) time(&now);
       pts = ctime(&now);
    char    *host = "127.0.0.1";        /* server IP to connect         */
//    char    *host = "172.18.33.155";
//    char    *host = "172.18.33.93";
//    char    *host = "172.18.187.1";
    char *service = "5050";          /* server port to connect       */
//    char *service = "50000";
    struct  sockaddr_in sin;            /* an Internet endpoint address    */
    WSADATA wsadata;
    WSAStartup(WSVERS, &wsadata);       /* 启动某版本Socket的DLL        */        

    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons((u_short)atoi(service));    //atoi:把ascii转化为int. htons:主机序(host)转化为网络序(network), s--short
    sin.sin_addr.s_addr = inet_addr(host);           //如果host为域名,需要先用函数gethostbyname把域名转化为IP地址

    sock = socket(PF_INET, SOCK_STREAM,0);

    connect(sock, (struct sockaddr *)&sin, sizeof(sin));

    printf("\t\t\t\tChat 多人聊天程序 \n");
    printf("\t\t\t\t       (Client) \n");
    hThread = (HANDLE)_beginthreadex(NULL, 0,Chat, NULL, 0, &threadID);    
    printf(" \t\t\t\t 【您可以自由发言】\n\n");
    while(1)
    {
        char buf1[2000];
        
        //     scanf("%s",&buf1);
        
             gets_s(buf1);
             if(!strcmp(buf1 ,"exit"))
                 goto end;

            (void) send(sock,buf1, sizeof(buf1), 0);
            (void) time(&now);
            pts = ctime(&now);
           printf(" 发送成功! ------时间: %s\n",pts);
    }
    
    end:    CloseHandle(hThread);
            closesocket(sock);
            WSACleanup();                     /* 卸载某版本的DLL */  
    
        printf("按回车键继续...");
        getchar();
        return 0;                           /* exit */
}

 

  • 4
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实现C/C++TCP服务器/客户端多人聊天室,需要以下步骤: 1. 确定通信协议 在TCP/IP协议中,服务器客户端之间的通信使用套接字(Socket)实现。服务器客户端之间的通信协议需要在客户端服务器之间进行协商,确定信息的格式和传输方式。 2. 设计服务器程序 服务器需要具有以下功能: - 监听客户端的连接请求。 - 接收客户端发送的信息,并将信息转发给其他客户端。 - 维护客户端连接状态,包括处理连接请求、处理断开连接请求等。 3. 设计客户端程序 客户端需要具有以下功能: - 连接服务器。 - 发送消息给服务器。 - 接收服务器转发的其他客户端发送的消息。 - 登录验证。 4. 实现用户账号密码用txt保存并读取 可以使用文件读写API实现用户账号密码用txt保存并读取。具体实现可以在服务器端或客户端实现。 下面是一个简单的C++代码实现多人聊天室服务器客户端服务器端代码: ```c++ #include <iostream> #include <string> #include <cstring> #include <cstdio> #include <cstdlib> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #include <pthread.h> #include <map> #define MAX_CLIENT_NUM 100 #define BUFF_SIZE 1024 using namespace std; struct ClientInfo { int sock_fd; string username; }; map<int, ClientInfo> client_list; // 客户端列表 pthread_mutex_t mutex_client_list; // 客户端列表的锁 void send_message(const char* message, int sender_sock_fd) { pthread_mutex_lock(&mutex_client_list); for (auto& client : client_list) { if (client.second.sock_fd != sender_sock_fd) { write(client.second.sock_fd, message, strlen(message)); } } pthread_mutex_unlock(&mutex_client_list); } void* handle_client(void* arg) { int sock_fd = *(int*)arg; char buff[BUFF_SIZE] = {0}; string username; // 接收客户端的用户名 read(sock_fd, buff, BUFF_SIZE); username = buff; pthread_mutex_lock(&mutex_client_list); ClientInfo client_info; client_info.sock_fd = sock_fd; client_info.username = username; client_list.insert(make_pair(sock_fd, client_info)); pthread_mutex_unlock(&mutex_client_list); printf("User %s connected.\n", username.c_str()); // 广播有新用户连接 string message = username + " joined the room.\n"; send_message(message.c_str(), sock_fd); // 循环处理客户端消息 while (true) { memset(buff, 0, sizeof(buff)); int recv_len = read(sock_fd, buff, BUFF_SIZE); if (recv_len <= 0) { // 客户端断开连接 pthread_mutex_lock(&mutex_client_list); client_list.erase(sock_fd); pthread_mutex_unlock(&mutex_client_list); printf("User %s disconnected.\n", username.c_str()); // 广播有用户断开连接 string message = username + " left the room.\n"; send_message(message.c_str(), sock_fd); close(sock_fd); break; } // 广播客户端发送的消息 message = username + ": " + buff; send_message(message.c_str(), sock_fd); } return NULL; } int main(int argc, char* argv[]) { if (argc != 2) { printf("Usage: %s <port>\n", argv[0]); exit(-1); } int server_sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (server_sock_fd < 0) { perror("socket"); exit(-1); } int port = atoi(argv[1]); sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); server_addr.sin_addr.s_addr = INADDR_ANY; if (bind(server_sock_fd, (sockaddr*)&server_addr, sizeof(server_addr)) < 0) { perror("bind"); exit(-1); } if (listen(server_sock_fd, MAX_CLIENT_NUM) < 0) { perror("listen"); exit(-1); } pthread_mutex_init(&mutex_client_list, NULL); while (true) { sockaddr_in client_addr; socklen_t client_addr_len = sizeof(client_addr); int client_sock_fd = accept(server_sock_fd, (sockaddr*)&client_addr, &client_addr_len); if (client_sock_fd < 0) { perror("accept"); continue; } pthread_t thread_id; pthread_create(&thread_id, NULL, handle_client, &client_sock_fd); pthread_detach(thread_id); } return 0; } ``` 客户端代码: ```c++ #include <iostream> #include <string> #include <cstring> #include <cstdio> #include <cstdlib> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #define BUFF_SIZE 1024 using namespace std; int main(int argc, char* argv[]) { if (argc != 3) { printf("Usage: %s <ip> <port>\n", argv[0]); exit(-1); } int sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (sock_fd < 0) { perror("socket"); exit(-1); } int port = atoi(argv[2]); sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); if (inet_pton(AF_INET, argv[1], &server_addr.sin_addr) < 0) { perror("inet_pton"); exit(-1); } if (connect(sock_fd, (sockaddr*)&server_addr, sizeof(server_addr)) < 0) { perror("connect"); exit(-1); } char buff[BUFF_SIZE] = {0}; string username, password; // 登录验证 printf("Please enter your username: "); cin >> username; printf("Please enter your password: "); cin >> password; sprintf(buff, "%s:%s", username.c_str(), password.c_str()); write(sock_fd, buff, strlen(buff)); // 循环处理用户输入和服务器消息 while (true) { fd_set read_set; FD_ZERO(&read_set); FD_SET(STDIN_FILENO, &read_set); FD_SET(sock_fd, &read_set); select(sock_fd + 1, &read_set, NULL, NULL, NULL); if (FD_ISSET(STDIN_FILENO, &read_set)) { // 从标准输入读取用户输入,发送给服务器 string message; getline(cin, message); sprintf(buff, "%s", message.c_str()); write(sock_fd, buff, strlen(buff)); } if (FD_ISSET(sock_fd, &read_set)) { // 从服务器读取消息,输出到标准输出 memset(buff, 0, sizeof(buff)); int recv_len = read(sock_fd, buff, BUFF_SIZE); if (recv_len <= 0) { printf("Disconnected from server.\n"); break; } printf("%s", buff); } } close(sock_fd); return 0; } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值