1. 程式人生 > >linux系統下一個基於TCP的網路聊天室

linux系統下一個基於TCP的網路聊天室

本聊天室程式在Ubuntu下,採用C語言實現,結構為Client/Server結構;

服務端程式通過共享儲存區儲存聊天資料,併發送給每個連線的客戶端;

服務端程式和客戶端程式都是通過父子程序分別負責傳送和接收資料的,以避免資料衝撞;

需按以下格式呼叫客戶端程式:client.exe 服務端主機IP 埠號(本程式設定為:3490) 使用者名稱(在聊天室中顯示的使用者名稱)

此處用的是:網路協議和基本的伺服器與客戶端之間的聯絡

原理就像我們平時所使用的QQ聊天軟體一樣,其實工作原理是類似的,只是增加了許多輔助功能

下面讓我們進入程式碼介面層層分析:

伺服器端
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h> //資料型別定義
#include<sys/stat.h>
#include<netinet/in.h> //定義資料結構sockaddr_in
#include<sys/socket.h> //提供socket函式及資料結構
#include<string.h>
#include<unistd.h>
#include<signal.h>
#include<sys/ipc.h>
#include<errno.h>
#include<sys/shm.h>
#include<time.h>
#define PERM S_IRUSR|S_IWUSR
#define MYPORT 3490 //巨集定義定義通訊埠
#define BACKLOG 10 //巨集定義,定義服務程式可以連線的最大客戶數量
#define WELCOME "|----------Welcome to the chat room! ----------|" //巨集定義,當客戶端連線服務端時,想客戶傳送此歡迎字串
//轉換函式,將int型別轉換成char *型別
void itoa(int i,char*string)
{
int power,j;
j=i;
for(power=1;j>=10;j/=10)
power*=10;
for(;power>0;power/=10)
{
*string++='0'+i/power;
i%=power;
}
*string='\0';
}

//得到當前系統時間
void get_cur_time(char * time_str)
{
time_t timep;
struct tm *p_curtime;
char *time_tmp;
time_tmp=(char *)malloc(2);
memset(time_tmp,0,2);

memset(time_str,0,20);
time(&timep);
p_curtime = localtime(&timep);
strcat(time_str," (");
itoa(p_curtime->tm_hour,time_tmp);
strcat(time_str,time_tmp);
strcat(time_str,":");
itoa(p_curtime->tm_min,time_tmp);
strcat(time_str,time_tmp);
strcat(time_str,":");
itoa(p_curtime->tm_sec,time_tmp);
strcat(time_str,time_tmp);
strcat(time_str,")");
free(time_tmp);
}
//建立共享儲存區,程序間通訊
key_t shm_create()
{
key_t shmid;
//shmid = shmget(IPC_PRIVATE,1024,PERM);
if((shmid = shmget(IPC_PRIVATE,1024,PERM)) == -1)
{
fprintf(stderr,"Create Share Memory Error:%s\n\a",strerror(errno));
exit(1);
}
return shmid;
}
//埠繫結函式,建立套接字,並繫結到指定埠
int bindPort(unsigned short int port)
{
int sockfd;
struct sockaddr_in my_addr;
sockfd = socket(AF_INET,SOCK_STREAM,0);//建立基於流套接字
my_addr.sin_family = AF_INET;//IPv4協議族
my_addr.sin_port = htons(port);//埠轉換
my_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(my_addr.sin_zero),0);//置空

if(bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr)) == -1)//繫結本地IP
{
perror("bind");
exit(1);
}
printf("bing success!\n");
return sockfd;
}
int main(int argc, char *argv[])
{
int sockfd,clientfd,sin_size,recvbytes; //定義監聽套接字、客戶套接字
pid_t pid,ppid; //定義父子執行緒標記變數
char *buf, *r_addr, *w_addr, *temp, *time_str;//="\0"; //定義臨時儲存區
struct sockaddr_in their_addr; //定義地址結構
key_t shmid;

shmid = shm_create(); //建立共享儲存區

temp = (char *)malloc(255);
time_str=(char *)malloc(20);
sockfd = bindPort(MYPORT);//繫結埠
while(1)
{
if(listen(sockfd,BACKLOG) == -1)//在指定埠上監聽
{
perror("listen");
exit(1);
}
printf("listening......\n");
if((clientfd = accept(sockfd,(struct sockaddr*)&their_addr,&sin_size)) == -1)//接收客戶端連線
{
perror("accept");
exit(1);
}
printf("accept from:%d\n",inet_ntoa(their_addr.sin_addr));
send(clientfd,WELCOME,strlen(WELCOME),0);//傳送問候資訊
buf = (char *)malloc(255);

ppid = fork();//建立子程序
if(ppid == 0)
{
//printf("ppid=0\n");
pid = fork(); //建立子程序
while(1)
{
if(pid > 0)
{
//父程序用於接收資訊
memset(buf,0,255);
//printf("recv\n");
//sleep(1);
if((recvbytes = recv(clientfd,buf,255,0)) <= 0)
{
perror("recv1");
close(clientfd);
raise(SIGKILL);
exit(1);
}
//write buf's data to share memory
w_addr = shmat(shmid, 0, 0);
memset(w_addr, '\0', 1024);
strncpy(w_addr, buf, 1024);
get_cur_time(time_str);
strcat(buf,time_str);
printf(" %s\n",buf);
}
else if(pid == 0)
{
//子程序用於傳送資訊
//scanf("%s",buf);
sleep(1);
r_addr = shmat(shmid, 0, 0);
//printf("---%s\n",r_addr);
//printf("cmp:%d\n",strcmp(temp,r_addr));
if(strcmp(temp,r_addr) != 0)
{
strcpy(temp,r_addr);
get_cur_time(time_str);
strcat(r_addr,time_str);
//printf("discriptor:%d\n",clientfd);
//if(send(clientfd,buf,strlen(buf),0) == -1)
if(send(clientfd,r_addr,strlen(r_addr),0) == -1)
{
perror("send");
}
memset(r_addr, '\0', 1024);
strcpy(r_addr,temp);
}
}
else
perror("fork");
}
}
}
printf("------------------------------\n");
free(buf);
close(sockfd);
close(clientfd);
return 0;
}

客戶端
#include<stdio.h>
#include<netinet/in.h> //定義資料結構sockaddr_in
#include<sys/socket.h> //提供socket函式及資料結構
#include<sys/types.h> //資料型別定義
#include<string.h>
#include<stdlib.h>
#include<netdb.h>
#include<unistd.h>
#include<signal.h>
#include<time.h>
int main(int argc, char *argv[])
{
struct sockaddr_in clientaddr;//定義地址結構
pid_t pid;
int clientfd,sendbytes,recvbytes;//定義客戶端套接字
struct hostent *host;
char *buf,*buf_r;
if(argc < 4)
{
printf("usage:\n");
printf("%s host port name\n",argv[0]);
exit(1);
}
host = gethostbyname(argv[1]);
if((clientfd = socket(AF_INET,SOCK_STREAM,0)) == -1) //建立客戶端套接字
{
perror("socket\n");
exit(1);
}
//繫結客戶端套接字
clientaddr.sin_family = AF_INET;
clientaddr.sin_port = htons((uint16_t)atoi(argv[2]));
clientaddr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(clientaddr.sin_zero),0);
if(connect(clientfd,(struct sockaddr *)&clientaddr,sizeof(struct sockaddr)) == -1) //連線服務端
{
perror("connect\n");
exit(1);
}
buf=(char *)malloc(120);
memset(buf,0,120);
buf_r=(char *)malloc(100);

if( recv(clientfd,buf,100,0) == -1)
{
perror("recv:");
exit(1);
}
printf("\n%s\n",buf);

pid = fork();//建立子程序
while(1)
{
if(pid > 0){
//父程序用於傳送資訊

//get_cur_time(time_str);

strcpy(buf,argv[3]);
strcat(buf,":");
memset(buf_r,0,100);
//gets(buf_r);
fgets(buf_r,100,stdin);
strncat(buf,buf_r,strlen(buf_r)-1);
//strcat(buf,time_str);
//printf("---%s\n",buf);
if((sendbytes = send(clientfd,buf,strlen(buf),0)) == -1)
{
perror("send\n");
exit(1);
}
}
else if(pid == 0)
{
//子程序用於接收資訊
memset(buf,0,100);
if(recv(clientfd,buf,100,0) <= 0)
{
perror("recv:");
close(clientfd);
raise(SIGSTOP);
exit(1);
}
printf("%s\n",buf);
}
else
perror("fork");
}
close(clientfd);
return 0;
}


以上是基於linux下的Ubuntu下實現的聊天功能,比較簡單;

1.設計客戶端定義埠號和ip地址,定義socket套接字,連線伺服器,傳送資料到伺服器。

2.設計客戶端:建立套接字,繫結ip和埠號,設計監聽,接受客戶端的連線,接受客戶端的訊息,回覆客戶端的訊息。

3.建立一個執行緒實現與客戶端的通訊