1、首先是服務器的創建數組
首先是一個信號終止程序,發信號ctrl+c終止程序,而是是初始化網絡通訊.服務器
建立一個描述符負責綁定服務器和監聽服務器接收客戶端的消息.網絡
socket()->sockaddr_in->bind->listen(準備就緒)socket
開始接收客戶端消息.start()函數函數
首先是聲明一個結構體用來存儲客戶端的消息,利用accept()函數來建立一個新的spa
描述符來接收,這裏有阻塞效果,也便是說鏈接的時候只能一個一個的連.線程
而後是分離線程處理這個sockfd的鏈接.code
pthread_create(&pid,0,pthread_deal,&sockfd1);server
線程主要是先作線程的分離,而後是取得它們的sockfd描述符,把這個客戶端的信息blog
存儲到一個結構體數組中.而後是調用循環發送到各個客戶端的函數,來發送消息.
當接收函數recv(sockfd,buf,sizeof(buf)) == 0 的時候,表示有客戶端退出,這時
就把這個在結構體數組的相應的fd置爲0
#include "server.h" #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> int sockfd; //服務器自己的描述符 struct User u[200] = {}; //用來保存用戶的信息 int size;//數組的下標,同時也是客戶端的個數 void init(void) //通訊準備工做 { printf("聊天室服務器立刻啓動....\n"); sockfd = socket(AF_INET,SOCK_STREAM,0); if(-1 == sockfd) { perror("fail to socket"); printf("服務器故障!\n"); exit(-1); } //準備通訊地址和綁定服務器 struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(2222); //端口的值,轉換成網絡的格式 addr.sin_addr.s_addr = inet_addr("172.16.1.21"); if(-1 == bind(sockfd,(struct sockaddr*)&addr,sizeof(addr))) { perror("fail to bind"); exit(-1); } printf("bind is ok,歡迎訪問!\n"); //設置監聽 if(-1 != listen(sockfd,200)) { printf("監聽已經設置,一切準備就緒!\n"); } } void send_msg(char *p_msg) //發送消息的函數 { int num = 0; for(num = 0;num < size;num++) { if(u[num].fd) //若是是有效的時候才發送 { send(u[num].fd,p_msg,sizeof(p_msg),0); } } } void* pthread_deal(void* p) //線程處理函數 { pthread_detach(pthread_self()); int fd2 = *(int*)p; //取得客戶端的sockfd u[size].fd = fd2; //放入結構體中 char name[20] = {}; int res = recv(fd2,name,sizeof(name),0); if(res > 0) { strcpy(u[size].name,name);//存放用戶名 } size++; char user[100] = {}; sprintf(user,"%s悄悄的進來了!(*^__^*) 嘻嘻……\n",name); send_msg(user); while(1) { if(recv(fd2,name,sizeof(name),0) == 0) /*返回0表示有客戶端退出*/ { u[size-1].fd = 0; //把退出的客戶端的結構描述符置換成0 } } } void start(void) { printf("success to start server,let's go!\n"); while(1) { struct sockaddr_in client;//存儲接收到的客戶端的信息 socklen_t length = sizeof(client); //接收客戶端的信息,會有阻塞效果,sockfd1標記客戶端 int sockfd1 = accept(sockfd,(struct sockaddr*)&client,&length); if(sockfd1 == -1) { perror("fail to accept!"); continue; //繼續鏈接 } printf("%s鏈接上來了\n",inet_ntoa(client.sin_addr)); /*鏈接成功以後,就啓動線程*/ pthread_t pid;//線程id pthread_create(&pid,0,pthread_deal,&sockfd1); } } void sig_exit(int signo ) //子定義信號關閉服務器函數 { close(sockfd);//關閉服務器端口 printf("服務器成功關閉!\n"); exit(0); } int main(void) { printf("按ctrl+c關閉聊天室服務器!\n"); signal(SIGINT,sig_exit); init();//初始化,服務器通訊準備工做 start();//啓動服務(開始處理聊天信息) return 0; }
2.客戶端的編寫.