C语言实现简易Linux终端版本聊天室

 实现的效果:服务器启动,监测客户端连接的个数,监测每个客户端的IP地址以及端口号,当每个客户端发送消息时,
 服务器上会有线程专门将每个客户端发送的信息记录在界面上,就类似平时使用QQ群聊一样。
 
1、实现一个基本的服务器和客户端的步骤
一、创建服务器的流程

(1)调用socket函数创建一个套接口,并返回描述符。

(2)调用bind函数使服务器进程与一个端口号绑定。

(3)调用listen设置客户端如队列的大小。

(4)调用accept接收一个连接,如果接入队列不为空的话。并且相应返回一个已连接的套接口描述符。

(5)调用send和recv用来在已连接的套接口间进行发送和接收数据。

二、创建客户端流程

(1)调用socket函数创建一个套接口,并返回描述符。

(2)调用connect向服务器发送连接请求,返回一个已连接的套接口。

(3)调用send和recv在已连接的套接口间发送和接收数据。


1.1服务器将要完成的工作
(1)获取套接字

(2)设置端口复用

(3)绑定连接的IP还有端口号

(4)监听

(5)创建一条线程用于显示客户端连接信息,具体连接的人数,顺便将客户连接的IP以及端口号打印出来。

(6)开始接收

(7)创建一条线程用于将客户端直接收发的信息分发到客户端处进行显示。

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <pthread.h>
#include <sys/time.h>
//设置客户端最大的个数为40个
#define   MAXCONNECTION        40
#define   msleep(x)     (usleep(x*1000))
struct  Data
{
     int  live ;   //0  无人用   1有人用
     int  sockfd ; 
     struct in_addr in ; 
     unsigned short  port ; 
};
 
struct Data array[MAXCONNECTION] = {0} ; 
void *do_thread_showconnect(void *arg);
void *do_thread_clientopt(void *arg);

int main(void)
{
    int sockfd ; 
    //1.获取套接字
    sockfd =  socket(AF_INET ,  SOCK_STREAM , 0);
    if(sockfd < 0)
    {
        perror("get socket fail");
        return -1 ; 
    }
    //2.设置端口复用
    int  on  = 4 ;  
    setsockopt(sockfd , SOL_SOCKET , SO_REUSEADDR , &on , sizeof(int));
    //3.绑定IP与端口
    struct  sockaddr_in  addr ; 
    addr.sin_family = AF_INET ; 
    addr.sin_port = htons(10000);
    //设置为INADDR_ANY,表示监听所有的。
    addr.sin_addr.s_addr = INADDR_ANY ; 
    int ret ; 
    ret = bind(sockfd , (struct sockaddr *)&addr , sizeof(struct sockaddr_in)) ; 
    if(ret < 0)
    {
        perror("bind error");
        return  -2 ; 
    }
    //4.监听
     listen(sockfd , 30);
     int  peersockfd ; 
     struct  sockaddr_in  peeraddr ; 
     socklen_t   len =   sizeof(struct sockaddr_in) ;    
     struct Data tmp ; 
     char *message = "too more connction , connect fail" ; 
     int i ; 
     pthread_t tid ; 
     //创建一条线程用于显示已连接的客户端的个数
     pthread_create(&tid , NULL , do_thread_showconnect , NULL);
     pthread_detach(tid);
 
     while(1)
     {
         peersockfd = accept(sockfd , (struct sockaddr *)&peeraddr , &len);
         if(peersockfd < 0)
         {
             perror("accept fail");
             return  -3 ; 
         }
         tmp.sockfd = peersockfd ;   
         tmp.in = peeraddr.sin_addr ;
         tmp.port = ntohs(peeraddr.sin_port);    
         tmp.live = 1 ; 
 
         for(i = 0 ; i < MAXCONNECTION ; i++)
         {
             if(array[i].live == 0)
             {
                 array[i] = tmp ; 
                 break;
             }
         }
         //判断是否连接个数已满
         if(i == MAXCONNECTION)
		 {
            write(peersockfd , message , strlen(message));
             close(peersockfd);
             continue ; 
         }
         //创建一条线程用于显示客户端之间互相发送的即时信息
         pthread_create(&tid , NULL , do_thread_clientopt , (void *)i);
         pthread_detach(tid);
     }
 
 
     return 0; 
 }
 
 //线程执行函数,用于显示已连接的客户端的个数。
void *do_thread_showconnect(void *arg)
{
    int  i , count = 0; 
    while(1)    
    {
        system("clear");
        printf("客户端连接信息:  连接人数:%d\n" , count);
        count = 0 ; 
        for(i = 0 ; i < MAXCONNECTION ; i++)
        {
            if(array[i].live == 1)
            {
                count++ ; 
                printf("IP:%s   port:%d \n" , inet_ntoa(array[i].in) , array[i].port);
            }
        }
        msleep(100);
    }
}
//线程执行函数,用于显示客户端之间互相发送的即时信息
void *do_thread_clientopt(void *arg)
{
    //转发信息
    int num = (int)arg ;
    char buffer[10240] = {0};
    char tmp[10240] = {0}; 
    int ret ; 
    struct  timeval  tv ; 
    struct  timezone tz ; 
    struct  tm *tt ; 
    int i ; 

    while(1)
    {
        ret = read(array[num].sockfd , tmp , 1024);
        if(ret <= 0)
            break;
        tmp[ret] = '\0'  ;
        gettimeofday(&tv , &tz);
        tt = localtime(&tv.tv_sec);
        sprintf(buffer , "%s @ %d:%d:%d :\n%s" ,inet_ntoa(array[num].in) , tt->tm_hour , tt->tm_min , tt->tm_sec , tmp);

        for(i = 0 ; i < MAXCONNECTION ; i++)
        {
            if(array[i].live == 1)
            {
                write(array[i].sockfd , buffer , strlen(buffer));
            }
        }
   }
    close(array[num].sockfd);
    array[num].live = 0 ; 

}

  服务端的工作已经设置完毕,显示就开始设置客户端吧,客户端就可以把它想象成我们的QQ群聊,只要每个人一发信息,那么整个群都可以看得到。

1.2客户端将要完成的工作

(1)连接对应的服务器,必须指定服务器的ip地址

(2)创建一条线程,用于读取从服务器转发过来的消息

(3)客户端可以自由的输入,通过服务器,发送给其它的客户端,让它们也可以看得到。

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <pthread.h>

void *do_thread(void * arg);

int main(void)
{
    int sd ;    

    sd = socket(AF_INET ,  SOCK_STREAM ,  0);
    if(sd < 0)
    {
        perror("get socket  fail");
        return -1 ;
    }
    //1.连接对应的服务器
    //connect
    struct sockaddr_in   addr ; 
    addr.sin_family = AF_INET ; 
    addr.sin_port = htons(10000);
   	addr.sin_addr.s_addr  =  inet_addr("101.132.71.152");
	//addr.sin_addr.s_addr  =  inet_addr("127.0.0.1");
    int ret ; 
    ret = connect(sd , (struct sockaddr *)&addr , sizeof(struct sockaddr_in));
    if(ret != 0)
    {
        perror("connect fail");
        return -3 ; 
    }
    printf("connect success ... \n");
    pthread_t tid ; 
    //创建一条线程用于接收从服务器端收到的数据
    pthread_create(&tid , NULL , do_thread , (void *)sd);
    pthread_detach(tid);

    char buffer[1024] = {0}; 

    while(1)
    {
        //阻塞从标准输出读取信息到buffer
        ret = read(0 , buffer , 1024);  
        if(ret > 1024)
            continue ; 
        //按下回车后将buffer中的内容写到文件描述符
        //通过服务器转发给其它正在连接的客户端
        write(sd, buffer , ret);
    }

    return 0 ; 
}
void *do_thread(void * arg)
{
    int sd = (int)arg;
    int ret ; 
    char buffer[1024] = {0};
    while(1)
    {
        //从服务器读取数据并显示在客户端上
        ret = read(sd , buffer , 1024);
        if(ret <= 0)
            break;
        buffer[ret] = '\0' ; 
        printf("recv:%s" , buffer);
    }
}

源码编写完毕,接下来测试一下这个简单聊天室的功能:编译过程省略,注意,该程序在32位操作系统上运行,且要加上线程库才可以编译成功。分别编译server.c和client.c

gcc server.c  -o  server  -m32  -lpthread
gcc client.c  -o  client  -m32  -lpthread


VT100控制码表

 1   具体格式有两种,
 2   一种数字形式,
 3    \033[<数字>m .
 4    如 \033[40m ,表示让后面字符输出用背景黑色输出 \033[0m 表示取消前面的设置。
 5   另一种是控制字符形式。
 6    \033[K 清除从光标到行尾的内容
 7    \033[nC 光标右移 n 行
 8    输出时, 也可以用 ^[来代替.
 9    VT100  控制码
10    VT100 控制码归类如下。
11    \033[0m 关闭所有属性 
12    \033[1m 设置高亮度 
13    \033[4m 下划线 
14    \033[5m 闪烁 
15    \033[7m 反显 
16    \033[8m 消隐 
17    \033[30m -- \033[37m 设置前景色 
18    \033[40m -- \033[47m 设置背景色 
19    \033[nA 光标上移 n 行 
20    \033[nB 光标下移 n 行 
21    \033[nC 光标右移 n 行 
22    \033[nD 光标左移 n 行 
23    \033[y;xH 设置光标位置 
24    \033[2J 清屏 
25    \033[K 清除从光标到行尾的内容 
26    \033[s 保存光标位置 
27    \033[u 恢复光标位置 
28    \033[?25l 隐藏光标 
29    \033[?25h 显示光标
30    VT100   关于颜色的说明:
31    VT100 的颜色输出分为,注意要同时输出前景的字符颜色和背景颜色。
32    字背景颜色范围:40----49 
33    40:黑 
34    41:深红 
35    42:绿 
36    43:黄色 
37    44:蓝色 
38    45:紫色 
39    46:深绿 
40    47:白色 
41    字颜色:30-----------39 
42    30:黑 
43    31:红 
44    32:绿 
45    33:黄 
46    34:蓝色 
47    35:紫色 
48    36:深绿 
49    37:白色
50    这样输出一个字符串比较完整如下
51    echo "\033[字背景颜色;字体颜色 m 字符串\033[0m"
52    例:
53    echo "\033[41;36m something here \033[0m"
1    例如:
2    C语言编程里可以这么用
3    设置光标位置 x=1 y=2
4    printf("\033[%d;%dH\033[43m \033[0m" ,1, 2);

实验效果:

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值