在Linux下基于TCP网络通信的多人在线聊天室

该博客介绍了基于TCP网络传输和多线程事件处理的服务端与客户端通信程序。阐述了服务端初始化、等待客户端连接及分配名称的功能,说明了单独和群发消息的使用方法,还提及编译时需加线程库,最后给出服务端代码及客户端调试方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、使用原理

TCP网络传输;多线程事件处理;

 

二、功能简介:(服务端)

1、打开服务端,服务端进行初始化,并等待客户端的连接;

2、打开客户端,输入服务端的IP地址与端口号;服务端会产生一个线程与新增的客户端进行通信,并分配客户端的名称,每连接一个客户端,服务端会产生一个线程与客户端进行通信;

3、每新建一个客户端服务端会为客户端分配一个名字,依次为: ‘a’,'b','c',.....

三、使用方法

1、单独发送信息:每个客户端发送的信息会保存在服务端的缓冲区中,例如

客户端a发送:

a hello;

即发送给自己消息:

hello;

客户端a发送:

b hello;

即发送给客户端b消息:

hello;

2、群发消息:

在发送的消息前面加上 ‘x’ ,即触发群发消息的功能;

 

四、编译   

由于使用了线程的功能,所以在编译的时候  gcc server.c -lpthead -o server  加上线程库;

 

五、代码(server.c)     客户端可以先使用      nc 127.0.0.1 9527    (服务端IP加端口号) 进行调试

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
//#include<ctype.h>
#include<unistd.h>
//#include<errno.h>
#include<pthread.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#define SERV_PORT 9527          /*服务端的端口号       */
#define ONLINEMAX 10             /*同时在线的最大人数   */

void *ChatOnline(void *arg);    /*线程的聊天,消息处理函数   */
void fuck_filter(const char* buf);
int UserSelect(char name);

/*用于用户信息的临时参数存储 ->  套接字的文件描述符 && IP+PORT  */
struct arg_user
{
        int cfd;
        struct sockaddr_in clit_addr;
};

/*用户信息的存放函数 ->用户名 && 套接字文件描述符    */
struct user
{
        char  name;
        int user_cfd;
}clit_user[ONLINEMAX];

/*报错函数  */
void sys_err(const char *str)
{
        perror(str);
        exit(1);
}

static int user_number = 0;     /* 用户的号数*/

int main()
{
        int sfd = 0,cfd = 0;
        int ret = 0,i = 0,tmp = 0;
        char buf[BUFSIZ];        //BUFSIZ大小为8192字节
        struct sockaddr_in serv_addr,clit_addr;
        struct arg_user *arg;
        socklen_t clit_addr_len;
        pthread_t pthread;

        /*设置 struct sockaddr_in 里面的内容 采用IPv4 端口号为9527 IP号由INADDR_ANY生成的二进制的IP地址   */
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(SERV_PORT);
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

        /*生成套接字,并校验*/
        sfd = socket(AF_INET,SOCK_STREAM,0);
        if(sfd == -1){
                sys_err("socket error");
        }
        printf("socket .....");

        /*绑定端口号+IP地址  */
        bind(sfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
        printf("bind .......");

        /*设置监听的上限        */
        listen(sfd,20);
        printf("listen .....");

        int opt = SO_REUSEADDR;
        setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
      /*setsockopt()用来设置参数s所指定的socket状态。
      参数level代表欲设置的网络层,一般设成SOL_SOCKET以存取socket层。
      参数optname代表欲设置的选项,有下列几种数值:SO_REUSEADDR 允许在bind()过程中本地地址可重复使用
        参数  optval代表欲设置的值,参数optlen则为optval的长度。
      返回值  成功则返回0,若有错误则返回-1,错误原因存于errno。*/
        /*监听,阻塞至有客户端连接,并返回一个新的套接字与客户端建立通讯 */

        clit_addr_len = sizeof(clit_addr);


        /*读取客户端发来的信息,并转化为大写    */
        while(1){
                printf("accept .....\n");
                cfd = accept(sfd,(struct sockaddr *)&clit_addr,&clit_addr_len);
                if(cfd == -1)   sys_err("accpet error");
//              printf("client's IP = %s",inet_ntoa(clit_addr.sin_addr));

/*指向结构体的结构体要先分配空间    将用户信息收集起来 */
                arg = malloc(sizeof(struct arg_user));
                arg->cfd = cfd;
                memcpy((void *)&arg->clit_addr, &clit_addr, sizeof(clit_addr));

                tmp = pthread_create(&pthread,NULL,ChatOnline,(void*)arg);
                if(tmp != 0)    sys_err("pthread_create faile");
        }

        close(sfd);
        close(cfd);
        return 0;
}

void *ChatOnline(void *arg)
{
        int cfd = 0;int ret = 0;
        struct sockaddr_in clit_addr;
        struct arg_user *n_arg;
        char buf[BUFSIZ];
//      char *p=NULL;
        char name;
        struct pid{
                int pid_pid;
                char pid_name;
        }my_pid;

        n_arg = malloc(sizeof(struct arg_user));
        n_arg = arg;
        cfd = n_arg->cfd;
        clit_addr = n_arg->clit_addr;

        clit_user[user_number].name = 'a'+user_number;
        clit_user[user_number].user_cfd = cfd;
        my_pid.pid_name = clit_user[user_number].name;
        if(user_number++ > ONLINEMAX) user_number = 0;

        printf("client's IP = %s...name = %c\n",inet_ntoa(clit_addr.sin_addr),my_pid.pid_name);
        my_pid.pid_pid = pthread_self();

        while(1){
                memset(buf,0,BUFSIZ);
                ret = read(cfd,buf,sizeof(buf));
                if(ret == 0 || ret == -1){
                        perror("client disconnec");
                        close(cfd);
                        break;
                }
                write(STDOUT_FILENO,buf,ret);

                fuck_filter(buf);       //话语过滤
                name = buf[0];

                if(name == 'x'){
                        int i;
                        for(i = 0;i < ONLINEMAX;i++){
                                if(clit_user[i].user_cfd > 0){
        //                              write(clit_user[i].user_cfd,"[THE MESSAGE From]:->",28);
        //                              write(clit_user[i].user_cfd,p,sizeof(p));
                                        write(clit_user[i].user_cfd,buf,ret);

                                }
                        }
                }
                int user_cfd = UserSelect(name);
                if(user_cfd > 0 ){
                        write(user_cfd,buf,ret);

                }
        }
}
int UserSelect(char name)
{
    int ufd = 0;
    int i;
    for(i=0;i<= ONLINEMAX;i++)
    {

        if(name == clit_user[i].name)
            return clit_user[i].user_cfd;
    }

    return ufd;
}


void fuck_filter(const char* buf)
{
    char buf_fuck[] = "fuck";
    char * p = NULL;
    do{
        p = strstr(buf,buf_fuck);
        if(p != 0){
            *p = '*';
            *(++p) = '*';
            *(++p) = '*';
            *(++p) = '*';
        }
    }while(p != 0);
}

 

一、实验目的 1.掌握通信规范的制定及实现。 2.练习较复杂的网络编程,能够把协议设计思想应用到现实应用中。 二、实验内容和要求 1.进一步熟悉VC++6编程环境; 2.利用VC++6进行较复杂的网络编程,完成网络聊天室的设计及编写; 三、实验(设计)仪器设备和材料 1.计算机及操作系统:PC机,Windows; 2.网络环境:可以访问互联网; 四、 TCP/IP程序设计基础 基于TCP/IP的通信基本上都是利用SOCKET套接字进行数据通讯,程序一般分为服务器端和用户端两部分。设计思路(VC6.0下): 第一部分 服务器端 一、创建服务器套接字(create)。 二、服务器套接字进行信息绑定(bind),并开始监听连接(listen)。 三、接受来自用户端的连接请求(accept)。 四、开始数据传输(send/receive)。 五、关闭套接字(closesocket)。 第二部分 客户端 一、创建客户套接字(create)。 二、与远程服务器进行连接(connect),如被接受则创建接收进程。 三、开始数据传输(send/receive)。 四、关闭套接字(closesocket)。 CSocket的编程步骤:(注意我们一定要在创建MFC程序第二步的时候选上Windows Socket选项,其中ServerSocket是服务器端用到的,ClientSocket是客户端用的。) (1)构造CSocket对象,如下例: CSocket ServerSocket; CSocket ClientSocket; (2)CSocket对象的Create函数用来创建Windows Socket,Create()函数会自行调用Bind()函数将此Socket绑定到指定的地址上面。如下例: ServerSocket.Create(823); //服务器端需要指定一个端口号,我们用823。 ClientSocket.Create(); //客户端不用指定端口号。 (3)现在已经创建完基本的Socket对象了,现在我们来启动它,对于服务器端,我们需要这个Socket不停的监听是否有来自于网络上的连接请求,如下例: ServerSocket.Listen(5);//参数5是表示我们的待处理Socket队列中最能有几个Socket。 (4)对于客户端我们就要实行连接了,具体实现如下例: ClientSocket.Connect(CString SerAddress,Unsinged int SerPort);//其中SerAddress是服务器的IP地址,SerPort是端口号。 (5)服务器是怎么来接受这份连接的呢?它会进一步调用Accept(ReceiveSocket)来接收它,而此时服务器端还须建立一个新的CSocket对象,用它来和客户端进行交流。如下例: CSocket ReceiveSocket; ServerSocket.Accept(ReceiveSocket); (6)如果想在两个程序之间接收或发送信息,MFC也提供了相应的函数。如下例: ServerSocket.Receive(String,Buffer); //String是你要发送的字符串,Buffer是发送字符串的缓冲区大小。ServerSocket.Send(String,Butter);//String是你要接收的字符串,Buffer是接收字符串的缓冲区大小。
<计算机网络实验> 基于TCP的网络聊天室的设计 -实验指导 一、实验目的 1.掌握通信规范的制定及实现。 2.练习较复杂的网络编程,能够把协议设计思想应用到现实应用中。 二、实验内容和要求 1.进一步熟悉VC++6编程环境; 2.利用VC++6进行较复杂的网络编程,完成网络聊天室的设计及编写; 三、实验(设计)仪器设备和材料 1.计算机及操作系统:PC机,Windows; 2.网络环境:可以访问互联网; 四、 TCP/IP程序设计基础 基于TCP/IP的通信基本上都是利用SOCKET套接字进行数据通讯,程序一般分为服务器端和用户端两部分。设计思路(VC6.0下): 第一部分 服务器端 一、创建服务器套接字(create)。 二、服务器套接字进行信息绑定(bind),并开始监听连接(listen)。 三、接受来自用户端的连接请求(accept)。 四、开始数据传输(send/receive)。 五、关闭套接字(closesocket)。 第二部分 客户端 一、创建客户套接字(create)。 二、与远程服务器进行连接(connect),如被接受则创建接收进程。 三、开始数据传输(send/receive)。 四、关闭套接字(closesocket)。 CSocket的编程步骤:(注意我们一定要在创建MFC程序第二步的时候选上Windows Socket选项,其中ServerSocket是服务器端用到的,ClientSocket是客户端用的。) (1)构造CSocket对象,如下例: CSocket ServerSocket; CSocket ClientSocket; (2)CSocket对象的Create函数用来创建Windows Socket,Create()函数会自行调用Bind()函数将此Socket绑定到指定的地址上面。如下例: ServerSocket.Create(823); //服务器端需要指定一个端口号,我们用823。 ClientSocket.Create(); //客户端不用指定端口号。 (3)现在已经创建完基本的Socket对象了,现在我们来启动它,对于服务器端,我们需要这个Socket不停的监听是否有来自于网络上的连接请求,如下例: ServerSocket.Listen(5);//参数5是表示我们的待处理Socket队列中最能有几个Socket。 (4)对于客户端我们就要实行连接了,具体实现如下例: ClientSocket.Connect(CString SerAddress,Unsinged int SerPort);//其中SerAddress是服务器的IP地址,SerPort是端口号。 (5)服务器是怎么来接受这份连接的呢?它会进一步调用Accept(ReceiveSocket)来接收它,而此时服务器端还须建立一个新的CSocket对象,用它来和客户端进行交流。如下例: CSocket ReceiveSocket; ServerSocket.Accept(ReceiveSocket); (6)如果想在两个程序之间接收或发送信息,MFC也提供了相应的函数。如下例: ServerSocket.Receive(String,Buffer); //String是你要发送的字符串,Buffer是发送字符串的缓冲区大小。ServerSocket.Send(String,Butter);//String是你要接收的字符串,Buffer是接收字符串的缓冲区大小。
里面包含聊天室的客户端和服务器端的源文件和一份完整的设计报告。 一、 系统概要 本系统能实现基于VC++的网络聊天室系统。有单独的客户端、服务器端。 服务器应用程序能够接受来自客户端的广播,然后向客户端发送本机的IP与服务端口,让客户端接入到服务器进行聊天,检测用户名是否合法(重复),服务器责接收来自客户端的聊天信息,并根据用户的需求发送给指定的或所有,能够给出上线下线提示。客户端能够发出连接请求,能编辑发送信息,可以指定发给单或所有,能显示聊天数,上线下线用户等。 二、 通信规范的制定 服务请求规范: 服务器端: (1) 创建一个UDP的套接字,接受来自客户端的广播请求,当请求报文内容为“REQUEST FOR IP ADDRESS AND SERVERPORT”时,接受请求,给客户端发送本服务器TCP聊天室的端口号。 (2) 创建一个主要的TCP协议的套接字负责客户端TCP连接 ,处理它的连接请求事件。 (3)在主要的TCP连接协议的套接字里面再创建TCP套接字保存到动态数组里,在主要的套接字接受请求后 ,就用这些套接字和客户端发送和接受数据。 客户端: (1) 当用户按“连接”按钮时,创建UDP协议套接字,给本地计算机发广播,广播内容为“REQUEST FOR IP ADDRESS AND SERVERPORT”。 (2)当收到服务器端的回应,收到服务器发来的端口号后,关闭UDP连接。根据服务器的IP地址和端口号重新创建TCP连接。 故我思考:客户端一定要知道服务器的一个端口,我假设它知道服务器UDP服务的端口,通过发广播给服务器的UDP服务套接字,然后等待该套接字发回服务器TCP聊天室服务的端口号,IP地址用ReceiveForom也苛刻得到。 通信规范 通信规范的制定主要跟老师给出的差不,并做了一小点增加: (增加验证用户名是否与聊天室已有用户重复,在服务器给客户端的消息中,增加标志0) ① TCP/IP数据通信 --- “聊天”消息传输格式 客户机 - 服务器 (1)传输“用户名” STX+1+用户名+ETX (2) 悄悄话 STX+2+用户名+”,”+内容+ETX (3) 对所有说 STX+3+内容+ETX 服务器- 客户机 (0)请求用户名与在线用户名重复 //改进 STX+0+用户名+EXT (1)首次传输在线用户名 STX+1+用户名+ETX (2)传输新到用户名 STX+2+用户名+ETX (3)传输离线用户名 STX+3+用户名+ETX (4)传输聊天数据 STX+4+内容+ETX (注:STX为CHR(2),ETX 为CHR(3)) 三、 主要模块的设计分析 四、 系统运行效果 (要求有屏幕截图) 五、 心得与体会
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值