/* net_select.c */
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <netinet/in.h>
#include<malloc.h>
#define PORT 4321
#define MAX_QUE_CONN_NM 5
#define MAX_SOCK_FD FD_SETSIZE
#define BUFFER_SIZE 1024
typedef struct user
{
struct sockaddr_in users_sockaddr;//使用者IP
int user_fd;//使用者串連通訊端
char *user_name;//使用者別名
char buf[BUFFER_SIZE];//使用者發的資訊
char del[4];//刪除下線使用者訊號標誌位
int rece_id;//目標使用者ID
struct user *Next;
}USER_List,*pUSER_List;
pUSER_List craet_USER_listhead()
{
pUSER_List pHead=(pUSER_List)malloc(sizeof(USER_List));
if(pHead==NULL)
{
printf("The Dynamic memory allocation is fail\n");
exit(-1);
}
pHead->Next=NULL;
return pHead;
}
pUSER_List insert_USERList(pUSER_List p,struct sockaddr_in uaddr,int fd,char *name)
{
int i=0;
pUSER_List pNew=(pUSER_List)malloc(sizeof(USER_List));
if(pNew==NULL)
{
printf("The Dynamic memory allocation is fail\n");
exit(-1);
}
pNew->users_sockaddr=uaddr;
pNew->user_fd=fd;
while(p->Next)
{
p=p->Next;
}
p->Next=pNew;
pNew->Next=NULL;
return;
}
void delet_List(pUSER_List p,int fd)
{
pUSER_List q;
do
{
q=p;//先儲存前一個地址
p=p->Next;
if(p->user_fd==fd)
{
q->Next=p->Next;
free(p);
}
}while(p->Next);
}
void sendlist_usr(pUSER_List p,int sockfd)
{
USER_List end,buff;
int sendbytes;
end.user_fd=6000;
//送訊息給用戶端
while(p->Next)
{
p=p->Next;
memset(&buff,0,sizeof(USER_List));
buff.users_sockaddr=p->users_sockaddr;
buff.user_fd=p->user_fd;
buff.user_name="w";
if ((sendbytes = send(sockfd,&buff,sizeof(USER_List), 0)) == -1)
{
perror("send");
exit(1);
}
}
if ((sendbytes = send(sockfd,&end,sizeof(USER_List), 0)) == -1)//發射結束標誌
{
perror("send");
exit(1);
}
}
void manypeople_chat_fun(pUSER_List USER,int fd,USER_List buff)
{
int sendbytes;
while(USER->Next)
{
USER=USER->Next;
printf("%d\n",USER->user_fd);
strcpy(USER->buf,buff.buf);
strcpy(USER->del,buff.del);
if(USER->user_fd!=fd)
{
if ((sendbytes=send(USER->user_fd,USER,sizeof(USER_List), 0)) == -1)
{
perror("send");
exit(1);
}
}
}
}
void singlepeople_chat_fun(int target,USER_List USER)
{
int sendbytes;
if ((sendbytes=send(target,&USER,sizeof(USER_List), 0)) == -1)
{
perror("send");
exit(1);
}
}
int main()
{
pUSER_List USER,REUSER;
int sendbytes;
struct sockaddr_in server_sockaddr, client_sockaddr;
int sin_size, count;
fd_set inset, tmp_inset;
int sockfd, client_fd, fd;
u_long hbuff;
char bufsym[5];
USER=craet_USER_listhead();//建立使用者列表檔案頭
REUSER=USER;
USER_List buff;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)//建立通訊端
{
perror("socket");
exit(1);
}
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_port = htons(PORT);
server_sockaddr.sin_addr.s_addr = INADDR_ANY;
bzero(&(server_sockaddr.sin_zero), 8);
int i = 1;/* 使得重複使用本地地址與通訊端進行綁定 */
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
if (bind(sockfd, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr)) == -1)
{
perror("bind");
exit(1);
}
printf("%s\n",(char *)inet_ntoa(&server_sockaddr.sin_addr));
if(listen(sockfd, MAX_QUE_CONN_NM) == -1)
{
perror("listen");
exit(1);
}
printf("listening....\n");
/*將調用socket函數的描述符作為檔案描述符*/
FD_ZERO(&inset);//初始化通訊端集合
FD_SET(sockfd, &inset);//select()機制中提供一fd_set的資料結構,實際上是一long類型的數組?
//每一個數組元素都能與一開啟的檔案控制代碼(不管是socket控制代碼,還是其他檔案或具名管道或裝置控制代碼)建立聯絡
while(1)
{
tmp_inset = inset;
sin_size=sizeof(struct sockaddr_in);
memset(&buff, 0, sizeof(USER_List));
/*調用select函數*/
printf("select....\n");
if (!(select(MAX_SOCK_FD, &tmp_inset, NULL, NULL, NULL) > 0))
{
perror("select");
close(sockfd);
exit(1);
}
for (fd = 0; fd < MAX_SOCK_FD; fd++)
{
if (FD_ISSET(fd, &tmp_inset) > 0)
{
if (fd == sockfd)
{ /* 服務端接收用戶端的串連請求 */
if ((client_fd = accept(sockfd, (struct sockaddr *)&client_sockaddr, &sin_size))== -1)
{
perror("accept");
exit(1);
}
FD_SET(client_fd, &inset);
strcpy(buff.del,"inl");//使用者上線標誌置位
manypeople_chat_fun(USER,fd,buff);//進入群聊模式把上線資訊發給說有線上使用者
strcpy(buff.del,"onl");
insert_USERList(USER,client_sockaddr,client_fd,"w");//插入登陸使用者資訊
printf("%d\n",USER->user_fd);
sendlist_usr(USER,client_fd);//把線上的使用者發給客戶機
//printf(" client IP:%s\n",(char*)inet_ntoa(client_sockaddr.sin_addr));
}
else /* 處理從用戶端發來的訊息 */
{
if ((count = recv(fd, &buff,sizeof(USER_List), 0)) > 0)
{
strncpy(bufsym,buff.buf,4);
if(strcmp(bufsym,"sinp")==0)//如果使用者選擇私聊
{
singlepeople_chat_fun(buff.rece_id,buff);
}
if(strcmp(bufsym,"manp")==0)//如果使用者選擇公聊
{
USER=REUSER;
manypeople_chat_fun(USER,fd,buff);
}
}
else
{
close(fd);
FD_CLR(fd, &inset);
delet_List(USER,fd);//將下線使用者刪除線上使用者列表
strcpy(buff.del,"del");
buff.rece_id=fd;
manypeople_chat_fun(USER,fd,buff);//給用戶端發送由使用者離開訊號
strcpy(buff.del,"onl");
printf("Client %d(socket) has left\n", fd);
}
}
} /* end of if FD_ISSET*/
} /* end of for fd*/
} /* end if while while*/
close(sockfd);
exit(0);
}