在和同學(xué)一起努力下終于完成了期末作業(yè)哈哈哈哈
文章目錄
目錄
前言
一、需求分析
二、功能設(shè)計
1.服務(wù)器端:
2.客戶端:
三、流程圖:
編程流程圖:
服務(wù)器流程圖:
客戶端流程圖:
四、運行效果:
項目源碼:
服務(wù)器源碼
客戶端源碼:
總結(jié):
前言
Linux網(wǎng)絡(luò)編程是我們這學(xué)期學(xué)習(xí)嵌入式的重要內(nèi)容
:不僅僅是期末作業(yè),更是對linux網(wǎng)絡(luò)編程的鞏固。
接下來我會將全部內(nèi)容分享出來哈哈ψ(`?′)ψ
提示:完成得比較隨意,不夠嚴(yán)謹(jǐn),僅供參考,大家都能做得更好!
一、需求分析
? ? ? ?需要一個可以實現(xiàn)多人同時在線的聊天室網(wǎng)絡(luò)應(yīng)用,用戶各自在登錄時自定義一個網(wǎng)名,并以這個網(wǎng)名在多人聊天室中與別人聊天,同時每個用戶可以通過用戶名辨別其他用戶。
? ? ? ?用戶還可以通過已知的用戶名,與指定用戶進行私聊(僅限對方可見)。
(走個流程)
二、功能設(shè)計
一個能容納多人在線聊天的聊天室的網(wǎng)絡(luò)應(yīng)用程序,包括客戶端與服務(wù)器端:
1.服務(wù)器端:
服務(wù)器端:能同時接受多用戶的登陸,并將每個用戶的聊天信息廣播到其他用戶;廣播的時候會附加上發(fā)言的用戶名;
2.客戶端:
客戶端:用戶能夠以` client name` 的命令行程序運行,并以`name`的用戶名登陸聊天室(不需要密碼);客戶端能允許用戶發(fā)言,并實時接收服務(wù)器的廣播信息;當(dāng)客戶端輸入`quit`時,退出客戶端;客戶端具備私聊的功能,當(dāng)A用戶需要向B用戶(假設(shè)`B`為用戶名)私聊時,使用命令`send:B:message` 發(fā)送聊天信息;服務(wù)器會把該信息發(fā)送給B用戶;
三、流程圖:
編程流程圖:
服務(wù)器流程圖:
客戶端流程圖:
四、運行效果:
先運行服務(wù)器端,然后在其他終端分別運行客戶端即可:
如圖:三個用戶分別進入聊天室,系統(tǒng)均會進行廣播通知,并顯示當(dāng)前在線人數(shù),當(dāng)其中一個客戶端運行時,會先注冊登錄(無需密碼)。
A?和 B 分別在群聊中發(fā)送消息,消息均在各個客戶端顯示;A 向 C 單獨發(fā)送私聊悄悄話,僅C的客戶端能看見A發(fā)來的消息,說明私聊功能實現(xiàn)。
截圖:
文章來源:http://www.zghlxwxcb.cn/news/detail-491315.html
項目源碼:
服務(wù)器源碼
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#define BUF_SIZE (4096)
#define SEM_SIZE (20) // 群聊上限人數(shù)
// 信號量--判斷群聊人數(shù)
sem_t sem;
// 服務(wù)端文件描述符
int svr_fd;
// 存儲群友,多一個是為了當(dāng)群人滿時,空一個出來接發(fā)信息
int cli_fd[SEM_SIZE + 1] = {};
struct client
{
/* data */
char buf[BUF_SIZE]; // message
char name[BUF_SIZE]; // name
int client_fd; // fd
};
struct client clients[SEM_SIZE];
// 字符串分割函數(shù)
void split(char *src, const char *separator, char **dest, int *num)
{
char *pNext;
// 記錄分隔符數(shù)量
int count = 0;
// 原字符串為空
if (src == NULL || strlen(src) == 0)
return;
// 未輸入分隔符
if (separator == NULL || strlen(separator) == 0)
return;
/*
c語言string庫中函數(shù),
聲明:
char *strtok(char *str, const char *delim)
參數(shù):
str -- 要被分解成一組小字符串的字符串。
delim -- 包含分隔符的 C 字符串。
返回值:
該函數(shù)返回被分解的第一個子字符串,如果沒有可檢索的字符串,則返回一個空指針。
*/
char *strtok(char *str, const char *delim);
// 獲得第一個由分隔符分割的字符串
pNext = strtok(src, separator);
while (pNext != NULL)
{
// 存入到目的字符串?dāng)?shù)組中
*dest++ = pNext;
++count;
/*
strtok()用來將字符串分割成一個個片段。參數(shù)s指向欲分割的字符串,參數(shù)delim則為分割字符串中包含的所有字符。
當(dāng)strtok()在參數(shù)s的字符串中發(fā)現(xiàn)參數(shù)delim中包涵的分割字符時,則會將該字符改為\0 字符。
在第一次調(diào)用時,strtok()必需給予參數(shù)s字符串,往后的調(diào)用則將參數(shù)s設(shè)置成NULL。
每次調(diào)用成功則返回指向被分割出片段的指針。
*/
pNext = strtok(NULL, separator);
}
*num = count;
}
// 群發(fā)函數(shù)
void *send_all(char *buf)
{
for (int i = 0; i < SEM_SIZE; i++)
{
// 若值為-1,則沒有此群友,表示已經(jīng)退出或未被占有
if (-1 != cli_fd[i])
{
printf("%s\n", buf);
printf("send to %d\n", cli_fd[i]);
write(cli_fd[i], buf, strlen(buf) + 1);
}
}
}
/**
* 發(fā)送給指定的用戶
* char *buf 發(fā)送的消息
* int fd 發(fā)給誰
*/
void *send_one(char *buf, int fd)
{
printf("send to %d : %s\n",fd, buf);
// printf("send to %d\n", fd);
write(fd, buf, strlen(buf) + 1);
}
// 服務(wù)端接收函數(shù)
void *server(void *arg)
{
int fd = *(int *)arg;
char buf[BUF_SIZE];
char name[BUF_SIZE], ts[BUF_SIZE];
// 獲取昵稱
read(fd, clients[fd].name, sizeof(name));
clients[fd].client_fd = fd;
// printf("clients[fd].name = %s\n", clients[fd].name);
printf("存放clients[fd].client_fd = %d\n", clients[fd].client_fd);
sprintf(ts, "[system]熱烈歡迎 %s 進入群聊", clients[fd].name);
send_all(ts);
for (;;)
{
// 接收信息,無信息時將阻塞
int recv_size = read(fd, clients[fd].buf, sizeof(buf));
// 收到退出信息
if (0 >= recv || NULL != strstr(clients[fd].buf, "quit"))
{
sprintf(ts, "[system]歡送 %s 離開群聊\n", clients[fd].name);
int index = 0;
// 找到要退出的那個人,并將其置為-1
for (; index < SEM_SIZE; index++)
{
if (cli_fd[index] == fd)
{
cli_fd[index] = -1;
break;
}
}
// 群發(fā)XXX退出聊天室提示消息
send_all(ts);
// 群友退出,信號量+1
int n;
sem_post(&sem);
sem_getvalue(&sem, &n);
printf("[system] %s 離開群聊,群聊還剩%d人\n", clients[fd].name, SEM_SIZE - n);
strcpy(clients[fd].buf, "quit");
write(fd, clients[fd].buf, strlen(clients[fd].buf) + 1);
close(fd);
pthread_exit(NULL);
}
// 單獨發(fā)送或者群發(fā)
// ------/send name message
if (0 >= recv || NULL != strstr(clients[fd].buf, "send"))// 單獨發(fā)
{
char str[100];
char *p[10]={0};
int num=0,i;
//拷貝
strcpy(str,clients[fd].buf);
// printf("clients[fd].buf1 = %s\n", clients[fd].buf); //send name message
// 對 clients[fd].buf進行截取
split(clients[fd].buf,":",p,&num);
// printf("clients[fd].buf1 = %s\n", clients[fd].buf); //send
// printf("split str is = %s\n",p[1]);
// 然后遍歷數(shù)組進行比較 找到和name相同的數(shù)組元素,取出來進行發(fā)送
for(i = 0;i < SEM_SIZE; i++) {
// 判斷名字,去除對應(yīng)client_fd進行發(fā)送
if(NULL != strstr(clients[i].name,p[2])){
printf("client[%d].name3 = %s\n",i,clients[i].name);
char msg[200];
sprintf(msg,"[悄悄話]%s:%s",clients[fd].name,p[3]);
send_one(msg, clients[i].client_fd);
}
}
}
else
{ // 群發(fā)
send_all(clients[fd].buf);
}
}
}
/**
* quit
*/
void sigint(int signum)
{
close(svr_fd);
sem_destroy(&sem);
printf("[system]服務(wù)器關(guān)閉\n");
exit(0);
}
int main()
{
signal(SIGINT, sigint);
// 初始化信號量,群聊上限SEM_SIZE人
sem_init(&sem, 0, SEM_SIZE);
// 創(chuàng)建socket對象
printf("[system]創(chuàng)建socket對象...\n");
svr_fd = socket(AF_INET, SOCK_STREAM, 0);
if (0 > svr_fd)
{
perror("socket");
return -1;
}
//端口復(fù)用函數(shù):解決端口號被系統(tǒng)占用的情況
int on = 1;
int gg = setsockopt(svr_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if(gg==-1)
{
perror("setsockopt");
return -1;
}
// 準(zhǔn)備通信地址(自己)
printf("[system]準(zhǔn)備通信地址...\n");
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(6666);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
socklen_t addrlen = sizeof(addr);
// 綁定socket對象與地址
printf("[system]綁定socket對象與地址...\n");
if (bind(svr_fd, (struct sockaddr *)&addr, addrlen))
{
perror("bind");
return -1;
}
// 設(shè)置監(jiān)聽和排除數(shù)量
printf("[system]設(shè)置監(jiān)聽");
if (listen(svr_fd, 10))
{
perror("listen");
return -1;
}
printf("[system]等待客戶端鏈接...\n");
// 將初始值置全為-1,表示該聊天位置沒有人占領(lǐng)
memset(cli_fd, -1, sizeof(cli_fd));
for (;;)
{
int sem_num;
sem_getvalue(&sem, &sem_num);
// 找到?jīng)]有人占領(lǐng)的聊天位
int index = 0;
while (-1 != cli_fd[index])
index++;
cli_fd[index] = accept(svr_fd, (struct sockaddr *)&addr, &addrlen);
if (0 > cli_fd[index])
{
perror("accept");
return -1;
}
char buf[BUF_SIZE];
if (0 >= sem_num)
{
printf("[system]人數(shù)已滿,%d號客戶端鏈接失敗\n", cli_fd[index]);
sprintf(buf, "[system]人數(shù)已滿,客戶端鏈接失敗");
write(cli_fd[index], buf, strlen(buf) + 1);
close(cli_fd[index]);
cli_fd[index] = -1;
}
else
{
sem_trywait(&sem);
sem_getvalue(&sem, &sem_num);
char msg[BUF_SIZE] = {};
printf("[system]%d號客戶端鏈接成功,當(dāng)前聊天人數(shù)%d人\n", cli_fd[index], SEM_SIZE - sem_num);
sprintf(msg, "[system]客戶端鏈接成功,當(dāng)前聊天人數(shù)%d人\n", SEM_SIZE - sem_num);
write(cli_fd[index], msg, strlen(msg) + 1);
// 創(chuàng)建線程客戶端
pthread_t tid;
pthread_create(&tid, NULL, server, &cli_fd[index]);
}
}
}
客戶端源碼:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#define BUF_SIZE (4096)
/**
* 讀取server轉(zhuǎn)發(fā)過來的消息
*/
void* client_read(void* arg)
{
int cli_fd = *(int*)arg;
char buf[BUF_SIZE];
//接收數(shù)據(jù)
for(;;)
{
int recv_size = read(cli_fd,buf,BUF_SIZE);
if(0 >= recv_size || 0 == strcmp(buf,"quit"))
{
printf("已經(jīng)與服務(wù)器斷開鏈接\n");
pthread_exit(NULL);
}
printf("%s\n",buf);
}
}
int main()
{
//創(chuàng)建socket對象
printf("創(chuàng)建socket對象...\n");
int cli_fd = socket(AF_INET,SOCK_STREAM,0);
if(0 > cli_fd)
{
perror("socket");
return -1;
}
//準(zhǔn)備通信地址(服務(wù)端)
printf("準(zhǔn)備通信地址...\n");
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(6666);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");//此處填寫自己的ip地址
socklen_t addrlen = sizeof(addr);
//鏈接服務(wù)端
printf("鏈接服務(wù)端口...\n");
if(connect(cli_fd,(struct sockaddr*)&addr,addrlen))
{
perror("connect");
return -1;
}
char buf[BUF_SIZE];
read(cli_fd,buf,BUF_SIZE);
if(NULL == strstr(buf,"鏈接成功"))
{
printf("群聊人已滿,請稍后再來\n");
close(cli_fd);
return 0;
}
printf("%s\n",buf);
//鏈接成功,創(chuàng)建客戶端
pthread_t tid;
pthread_create(&tid,NULL,client_read,&cli_fd);
//輸入昵稱
char name[BUF_SIZE] = {};
printf("請輸入你的昵稱:");
gets(name);
write(cli_fd,name,strlen(name)+1);
//發(fā)送數(shù)據(jù)
for(;;)
{
printf(">>");
gets(buf); // 獲取鍵盤字符串
char msg[BUF_SIZE]; // 存儲格式化后的字符串
sprintf(msg,"%s:%s",name,buf); // 輸出字符串
//把msg發(fā)送到cli_fd
int send_size = write(cli_fd,msg,strlen(msg)+1);
// 如果字符串是quit就退出
if(0 >= send_size || 0 == strcmp(buf,"quit"))
{
printf("結(jié)束通信\n");
close(cli_fd);
pthread_exit(NULL);
return 0;
}
}
}
總結(jié):
太好咯,又做完一個作業(yè) 哈哈哈O(∩_∩)O文章來源地址http://www.zghlxwxcb.cn/news/detail-491315.html
到了這里,關(guān)于多人聊天室(帶私聊功能)Linux網(wǎng)絡(luò)編程基礎(chǔ)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!