当前位置:首页 > 嵌入式 > 嵌入式云IOT技术圈
[导读]今天我们来实现一个简单的小项目,在这个项目中,我们将实现一个终端版的简易Linux聊天室。


微信公众号:嵌入式开发圈
关注可了解更多的教程。问题或建议,请公众号留言;
如果你觉得本文对你有帮助,欢迎赞赏


▲长按图片保存可分享至朋友圈


        

简易Linux终端聊天室

       

    今天我们来实现一个简单的小项目,在这个项目中,我们将实现一个终端版的简易Linux聊天室。  

    实现的效果:服务器启动,监测客户端连接的个数,监测每个客户端的IP地址以及端口号,当每个客户端发送消息时,服务器上会有线程专门将每个客户端发送的信息记录在界面上,就类似平时使用QQ群聊一样。我们来看看这个简易的Linux聊天室如何来实现吧。如图4-5-12所示。

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)创建一条线程用于将客户端直接收发的信息分发到客户端处进行显示。


下面具体看看服务器代码的实现  server.c

 1#include 
2#include 
3#include 
4#include 
5#include 
6#include 
7#include 
8#include 
9//设置客户端最大的个数为40个
10#define   MAXCONNECTION        40
11#define   msleep(x)     (usleep(x*1000))
12struct  Data
13{

14    int  live ;   //0  无人用   1有人用
15    int  sockfd ; 
16    struct in_addr in ; 
17    unsigned short  port ; 
18};
19
20struct Data array[MAXCONNECTION] = {0} ; 
21void *do_thread_showconnect(void *arg);
22void *do_thread_clientopt(void *arg);
23int main(void)
24
{
25    int sockfd ; 
26    //1.获取套接字
27    sockfd =  socket(AF_INET ,  SOCK_STREAM , 0);
28    if(sockfd < 0)
29    {
30        perror("get socket fail");
31        return -1 ; 
32    }
33    //2.设置端口复用
34    int  on  = 4 ;  
35    setsockopt(sockfd , SOL_SOCKET , SO_REUSEADDR , &on , sizeof(int));
36    //3.绑定IP与端口
37    struct  sockaddr_in  addr ; 
38    addr.sin_family = AF_INET ; 
39    addr.sin_port = htons(10086);
40    //设置为INADDR_ANY,表示监听所有的。
41    addr.sin_addr.s_addr = INADDR_ANY ; 
42    int ret ; 
43    ret = bind(sockfd , (struct sockaddr *)&addr , sizeof(struct sockaddr_in)) ; 
44    if(ret < 0)
45    {
46        perror("bind error");
47        return  -2 ; 
48    }
49    //4.监听
50    listen(sockfd , 30);
51    int  peersockfd ; 
52    struct  sockaddr_in  peeraddr ; 
53    socklen_t   len =   sizeof(struct sockaddr_in) ;    
54    struct Data tmp ; 
55    char *message = "too more connction , connect fail" ; 
56    int i ; 
57    pthread_t tid ; 
58    //创建一条线程用于显示已连接的客户端的个数
59    pthread_create(&tid , NULL , do_thread_showconnect , NULL);
60    pthread_detach(tid);
61
62    while(1)
63    {
64        peersockfd = accept(sockfd , (struct sockaddr *)&peeraddr , &len);
65        if(peersockfd < 0)
66        {
67            perror("accept fail");
68            return  -3 ; 
69        }
70        tmp.sockfd = peersockfd ;   
71        tmp.in = peeraddr.sin_addr ;
72        tmp.port = ntohs(peeraddr.sin_port);    
73        tmp.live = 1 ; 
74
75        for(i = 0 ; i < MAXCONNECTION ; i++)
76        {
77            if(array[i].live == 0)
78            {
79                array[i] = tmp ; 
80                break;
81            }
82        }
83        //判断是否连接个数已满
84        if(i == MAXCONNECTION)
85        {
86            write(peersockfd , message , strlen(message));
87            close(peersockfd);
88            continue ; 
89        }
90        //创建一条线程用于显示客户端之间互相发送的即时信息
91        pthread_create(&tid , NULL , do_thread_clientopt , (void *)i);
92        pthread_detach(tid);
93    }
94
95
96    return 0 ; 
97}
98
99//线程执行函数,用于显示已连接的客户端的个数。
100void *do_thread_showconnect(void *arg)
101
{
102    int  i , count = 0
103    while(1)    
104    {
105        system("clear");
106        printf("客户端连接信息:  连接人数:%d\n" , count);
107        count = 0 ; 
108        for(i = 0 ; i < MAXCONNECTION ; i++)
109        {
110            if(array[i].live == 1)
111            {
112                count++ ; 
113                printf("IP:%s   port:%d \n" , inet_ntoa(array[i].in) , array[i].port);
114            }
115        }
116        msleep(100);
117    }
118}
119//线程执行函数,用于显示客户端之间互相发送的即时信息
120void *do_thread_clientopt(void *arg)
121
{
122    //转发信息
123    int num = (int)arg ;
124    char buffer[12240] = {0};
125    char tmp[10240] = {0}; 
126    int ret ; 
127    struct  timeval  tv ; 
128    struct  timezone tz ; 
129    struct  tm *tt ; 
130    int i ; 
131
132    while(1)
133    {
134        ret = read(array[num].sockfd , tmp , 1024);
135        if(ret <= 0)
136            break;
137        tmp[ret] = '\0'  ;
138        gettimeofday(&tv , &tz);
139        tt = localtime(&tv.tv_sec);
140        sprintf(buffer , "%s @ %d:%d:%d :\n%s" ,inet_ntoa(array[num].in) , tt->tm_hour , tt->tm_min , tt->tm_sec , tmp);
141
142        for(i = 0 ; i < MAXCONNECTION ; i++)
143        {
144            if(array[i].live == 1)
145            {
146                write(array[i].sockfd , buffer , strlen(buffer));
147            }
148        }
149    }
150    close(array[num].sockfd);
151    array[num].live = 0 ; 
152
153}

    

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


1.2客户端将要完成的工作

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

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

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

下面具体看看客户端代码的实现  client.c

 1#include 
2#include 
3#include 
4#include 
5#include 
6void *do_thread(void * arg);
7int main(void)
8
{
9    int sd ;    
10
11    sd = socket(AF_INET ,  SOCK_STREAM ,  0);
12    if(sd < 0)
13    {
14        perror("get socket  fail");
15        return -1 ;
16    }
17    //1.连接对应的服务器
18    //connect
19    struct sockaddr_in   addr ; 
20    addr.sin_family = AF_INET ; 
21    addr.sin_port = htons(10086);
22    addr.sin_addr.s_addr  =  inet_addr("10.126.72.56");
23
24    int ret ; 
25    ret = connect(sd , (struct sockaddr *)&addr , sizeof(struct sockaddr_in));
26    if(ret != 0)
27    {
28        perror("connect fail");
29        return -3 ; 
30    }
31    printf("connect success ... \n");
32    pthread_t tid ; 
33    //创建一条线程用于接收从服务器端收到的数据
34    pthread_create(&tid , NULL , do_thread , (void *)sd);
35    pthread_detach(tid);
36
37    char buffer[1024] = {0}; 
38
39    while(1)
40    {
41        //阻塞从标准输出读取信息到buffer
42        ret = read(0 , buffer , 1024);  
43        if(ret > 1024)
44            continue ; 
45        //按下回车后将buffer中的内容写到文件描述符
46        //通过服务器转发给其它正在连接的客户端
47        write(sd, buffer , ret);
48    }
49
50    return 0 ; 
51}
52void *do_thread(void * arg)
53
{
54    int sd = (int)arg;
55    int ret ; 
56    char buffer[1024] = {0};
57    while(1)
58    {
59        //从服务器读取数据并显示在客户端上
60        ret = read(sd , buffer , 1024);
61        if(ret <= 0)
62            break;
63        buffer[ret] = '\0' ; 
64        printf("recv:%s" , buffer);
65    }
66}

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

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

下面先运行服务器,执行./server如图4-5-13所示。

下面启动不同IP的客户端,找多一台电脑即可测试。在我方47服务器上执行客户端./client,如图4-5-14所示。客户端连接成功了!  

接下来看看服务器上有什么变化,如图4-5-15所示。

 在我方56服务器上执行客户端./client,如图4-5-16所示。

接下来看看服务器上有什么变化,如图4-5-17所示。

    在47服务器上的客户端发送一条消息给56服务器上的客户端,同样的在56服务器上的客户端也发送一条信息给47的服务器上的客户端,观察变化,如图4-5-18所示。

    在这里我们看到,56服务器上的客户端发送hello world的消息给47服务器上的客户端,47服务器上的客户端也收到了helloworld消息,同样的,47服务器上的客户端给56服务器上的客户端发送I am 47 server的消息,56服务器上的客户端也收到了I am 47 server的消息。这个简易版本的Linux聊天室就算完成了,接下来,请读者发挥自己的想象力,结合VT100控制码,写出一个更漂亮的终端版聊天工具吧。

VT100控制码表

 1具体格式有两种,
2•  一种数字形式,
3\033[<数字>m .
4如 \033[40m ,表示让后面字符输出用背景黑色输出 \033[0m 表示取消前面的设置。
5•  另一种是控制字符形式。
6\033[K 清除从光标到行尾的内容
7\033[nC 光标右移 n 行
8输出时, 也可以用 ^[来代替.
9VT100  控制码
10VT100 控制码归类如下。
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 显示光标
30VT100   关于颜色的说明:
31VT100 的颜色输出分为,注意要同时输出前景的字符颜色和背景颜色。
32字背景颜色范围:40----49 
3340:黑 
3441:深红 
3542:绿 
3643:黄色 
3744:蓝色 
3845:紫色 
3946:深绿 
4047:白色 
41字颜色:30-----------39 
4230:黑 
4331:红 
4432:绿 
4533:黄 
4634:蓝色 
4735:紫色 
4836:深绿 
4937:白色
50这样输出一个字符串比较完整如下
51echo "\033[字背景颜色;字体颜色 m 字符串\033[0m"
52例:
53echo "\033[41;36m something here \033[0m"

1例如:
2C语言编程里可以这么用
3设置光标位置 x=1 y=2
4printf("\033[%d;%dH\033[43m \033[0m" ,12);

下一节,我将带领大家使用VT100控制码来实现一个好玩的方块。

另外推荐相关课程:

  韦东山老师优质嵌入式学习干货推荐:包括ARM裸机开发、Linux设备驱动程序、Linux应用程序开发、Android系统学习、Linux设备树等。


在我这里购买韦东山老师的课程还可得到本人的技术支持,手把手带你学习嵌入式!

王争老师优秀数据结构算法学习课程推荐




免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

特朗普集团近日取消了其新推出的T1智能手机“将在美国制造”的宣传标语,此举源于外界对这款手机能否以当前定价在美国本土生产的质疑。

关键字: 特朗普 苹果 AI

美国总统特朗普在公开场合表示,他已要求苹果公司CEO蒂姆·库克停止在印度建厂,矛头直指该公司生产多元化的计划。

关键字: 特朗普 苹果 AI

4月10日消息,据媒体报道,美国总统特朗普宣布,美国对部分贸易伙伴暂停90天执行新关税政策,同时对中国的关税提高到125%,该消息公布后苹果股价飙升了15%。这次反弹使苹果市值增加了4000多亿美元,目前苹果市值接近3万...

关键字: 特朗普 AI 人工智能 特斯拉

3月25日消息,据报道,当地时间3月20日,美国总统特朗普在社交媒体平台“真实社交”上发文写道:“那些被抓到破坏特斯拉的人,将有很大可能被判入狱长达20年,这包括资助(破坏特斯拉汽车)者,我们正在寻找你。”

关键字: 特朗普 AI 人工智能 特斯拉

1月22日消息,刚刚,新任美国总统特朗普放出重磅消息,将全力支持美国AI发展。

关键字: 特朗普 AI 人工智能

特朗普先生有两件事一定会载入史册,一个是筑墙,一个是挖坑。在美墨边境筑墙的口号确保边境安全,降低因非法移民引起的犯罪率过高问题;在中美科技产业之间挖坑的口号也是安全,美国企业不得使用对美国国家安全构成威胁的电信设备,总统...

关键字: 特朗普 孤立主义 科技产业

据路透社1月17日消息显示,知情人士透露,特朗普已通知英特尔、铠侠在内的几家华为供应商,将要撤销其对华为的出货的部分许可证,同时将拒绝其他数十个向华为供货的申请。据透露,共有4家公司的8份许可被撤销。另外,相关公司收到撤...

关键字: 华为 芯片 特朗普

曾在2018年时被美国总统特朗普称作“世界第八奇迹”的富士康集团在美国威斯康星州投资建设的LCD显示屏工厂项目,如今却因为富士康将项目大幅缩水并拒绝签订新的合同而陷入了僵局。这也导致富士康无法从当地政府那里获得约40亿美...

关键字: 特朗普 富士康

今年5月,因自己发布的推文被贴上“无确凿依据”标签而与推特发生激烈争执后,美国总统特朗普签署了一项行政令,下令要求重审《通信规范法》第230条。

关键字: 谷歌 facebook 特朗普

众所周知,寄往白宫的所有邮件在到达白宫之前都会在他地进行分类和筛选。9月19日,根据美国相关执法官员的通报,本周早些时候,执法人员截获了一个寄给特朗普总统的包裹,该包裹内包含蓖麻毒蛋白。

关键字: 美国 白宫 特朗普
关闭