【C語言】網路程式設計之簡單聊天室(socket、tcp)
阿新 • • 發佈:2020-12-19
技術標籤:網路程式設計小專案網路socket聊天室tcptcpip
網路聊天室業務邏輯:
1、客戶端註冊名字
2、告訴所有的線上的客戶端,XXX進入聊天室。
3、新建一個執行緒為該客戶端服務,隨時接收客戶端傳送來的內容。
4、當收到一個客戶端的訊息時,向每個客戶端都轉發一份(群聊)。
5、同時線上人數最多50人。
注意:任何客戶端都應該可以隨時進入退出。
如果對socket、tcp瞭解不透徹的童鞋可以看看:tcp網路知識傳送門、sokect傳送門
注意:ip地址需要填寫自己的IP地址,有自己的伺服器的童鞋可以試用一下自己的雲伺服器,記得改為自己服務的埠
執行:
gcc server.c -oserver
先執行server,在執行client(幾個人聊天就可以開幾個client程序)
需要調大群聊人數課調整server.c中的巨集SEM_SIZE(方便測試,我只設定了5個人);
直接上程式碼
客戶端
#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)
void* client_read(void* arg)
{
int cli_fd = *(int*)arg;
char buf[BUF_SIZE];
//接受資料
for(;;)
{
int recv_size = read(cli_fd,buf,BUF_SIZE);
if(0 >= recv_size || 0 == strcmp(buf,"quit"))
{
printf("已經與伺服器斷開連結\n" );
pthread_exit(NULL);
}
printf("%s\n",buf);
}
}
int main()
{
//建立socket物件
printf("建立socket物件...\n");
int cli_fd = socket(AF_INET,SOCK_STREAM,0);
if(0 > cli_fd)
{
perror("socket");
return -1;
}
//準備通訊地址(服務端)
printf("準備通訊地址...\n");
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(6789);
addr.sin_addr.s_addr = inet_addr("10.0.2.15");//此處填寫自己的ip地址
socklen_t addrlen = sizeof(addr);
//連結服務端
printf("連結服務埠...\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);
//連結成功,建立客戶端
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);
//傳送資料
for(;;)
{
printf(">>");
gets(buf);
char msg[BUF_SIZE];
sprintf(msg,"%s:%s",name,buf);
int send_size = write(cli_fd,msg,strlen(msg)+1);
if(0 >= send_size || 0 == strcmp(buf,"quit"))
{
printf("結束通訊\n");
close(cli_fd);
pthread_exit(NULL);
return 0;
}
}
}
服務端
#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 (5 ) //群聊上限人數
//訊號量--判斷群聊人數
sem_t sem;
//服務端檔案描述符
int svr_fd;
//儲存群友,多一個是為了當群人滿時,空一個出來接發信息
int cli_fd[SEM_SIZE+1] = {};
//群發函數
void* send_all(char* buf)
{
for(int i=0; i<SEM_SIZE; i++)
{
//若值為-1,則沒有此群友,表示已經退出或未被佔有
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);
}
}
}
//服務端接收函式
void* server(void* arg)
{
int fd = *(int*)arg;
char buf[BUF_SIZE];
char name[BUF_SIZE],ts[BUF_SIZE];
//獲取暱稱
read(fd,name,sizeof(name));
sprintf(ts,"熱烈歡迎 %s 進入群聊",name);
send_all(ts);
for(;;)
{
//接收資訊
int recv_size = read(fd,buf,sizeof(buf));
//收到退出資訊
if(0 >= recv || NULL != strstr(buf,"quit"))
{
sprintf(ts,"歡送 %s 離開群聊\n",name);
int index = 0;
//找到要退出的那個人,並將其置為-1
for(; index < SEM_SIZE; index++)
{
if(cli_fd[index] == fd)
{
cli_fd[index] = -1;
break;
}
}
send_all(ts);
//群友退出,訊號量+1
int n;
sem_post(&sem);
sem_getvalue(&sem,&n);
printf("%s 離開群聊,群聊還剩%d人\n",name,SEM_SIZE-n);
strcpy(buf,"quit");
write(fd,buf,strlen(buf)+1);
close(fd);
pthread_exit(NULL);
}
send_all(buf);
}
}
void sigint(int signum)
{
close(svr_fd);
sem_destroy(&sem);
printf("伺服器關閉\n");
exit(0);
}
int main()
{
signal(SIGINT,sigint);
//初始化訊號量,群聊上限SEM_SIZE人
sem_init(&sem,0,SEM_SIZE);
//建立socket物件
printf("建立socket物件...\n");
svr_fd = socket(AF_INET,SOCK_STREAM,0);
if(0 > svr_fd)
{
perror("socket");
return -1;
}
//準備通訊地址(自己)
printf("準備通訊地址...\n");
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(6789);
addr.sin_addr.s_addr = inet_addr("10.0.2.15");
socklen_t addrlen = sizeof(addr);
//繫結socket物件與地址
printf("繫結socket物件與地址...\n");
if(bind(svr_fd,(struct sockaddr*)&addr,addrlen))
{
perror("bind");
return -1;
}
//設定監聽和排除數量
printf("設定監聽");
if(listen(svr_fd,10))
{
perror("listen");
return -1;
}
printf("等待客戶端連結...\n");
//將初始值置全為-1,表示該聊天位置沒有人佔領
memset(cli_fd,-1,sizeof(cli_fd));
for(;;)
{
int sem_num;
sem_getvalue(&sem,&sem_num);
//找到沒有人佔領的聊天位
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("人數已滿,%d號客戶端連結失敗\n",cli_fd[index]);
sprintf(buf,"人數已滿,客戶端連結失敗");
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("%d號客戶端連結成功,當前聊天人數%d人\n",cli_fd[index],SEM_SIZE-sem_num);
sprintf(msg,"客戶端連結成功,當前聊天人數%d人\n",SEM_SIZE-sem_num);
write(cli_fd[index],msg,strlen(msg)+1);
//建立執行緒客戶端
pthread_t tid;
pthread_create(&tid,NULL,server,&cli_fd[index]);
}
}
}