一、首先是服務器的建立
首先是一個信號終止程序,發信號ctrl+c終止程序,而是是初始化網絡通信.
創建一個描述符負責綁定服務器和監聽服務器接收客戶端的消息.
socket()->sockaddr_in->bind->listen(准備就緒)
開始接收客戶端消息.start()函數
首先是聲明一個結構體用來存儲客戶端的消息,利用accept()函數來創建一個新的
描述符來接收,這里有阻塞效果,也即是說連接的時候只能一個一個的連.
然后是分離線程處理這個sockfd的連接.
pthread_create(&pid,0,pthread_deal,&sockfd1);
線程主要是先做線程的分離,然后是取得它們的sockfd描述符,把這個客戶端的信息
存儲到一個結構體數組中.然后是調用循環發送到各個客戶端的函數,來發送消息.
當接收函數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.客戶端的編寫.