#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/select.h>
typedef struct{
int *fd; //文件描述符數組
int count; //數組元素個數
int max_count; //fd對應空間大小
}st_fd_def;
/*
將fd接收套接字描述符放入到fd數組中
*/
int add_fd(st_fd_def* p_st_fd, int fd)
{
int i = 0;
/*
校驗當前文件描述符個數等於最大分配空間時
重新申請新的fd大小+3
*/
if (p_st_fd->count == p_st_fd->max_count)
{
int *p_fd = (int*)calloc(p_st_fd->max_count+3, sizeof(int));
if (p_fd == NULL)
{
printf("calloc memory error\n");
return -1;
}
memcpy(p_fd, p_st_fd->fd, sizeof(int)*p_st_fd->count);
free(p_st_fd->fd);
p_st_fd->fd = p_fd;
p_st_fd->max_count += 3;
}
p_st_fd->fd[p_st_fd->count++] = fd;
printf("fd=%d, count===%d, max_count===%d\n", fd, p_st_fd->count, p_st_fd->max_count);
return 0;
}
int get_max_fd(st_fd_def* p_st_fd, fd_set* p_fs)
{
int i = 0;
int max_fd = 0;
max_fd = p_st_fd->fd[0];
/*
* 遍歷fd數組,將各元素添加到描述符集中
* 並返回數組中最大描述符
*/
for(i = 0; i<p_st_fd->count; i++)
{
FD_SET(p_st_fd->fd[i], p_fs);
if (p_st_fd->fd[i] > max_fd)
{
max_fd = p_st_fd->fd[i];
}
}
return max_fd;
}
int remove_fd(st_fd_def* p_st_fd, int fd)
{
int i = 0;
int j = 0;
/*
刪除fd數組中對應元素的值
*/
for (; i<p_st_fd->count; i++)
{
if (p_st_fd->fd[i] == fd)
{
for(j = i; j<p_st_fd->count; j++)
{
p_st_fd->fd[j] = p_st_fd->fd[j+1];
if (j == p_st_fd->count-1)
{
p_st_fd->fd[j] = -1;
}
}
}
}
p_st_fd->count--;
return i;
}
int destroy_fd(st_fd_def* p_st_fd)
{
if (p_st_fd->fd != NULL)
{
free(p_st_fd->fd);
free(p_st_fd-;
}
return 0;
}
void* thread_fun(void* arg)
{
int max_fd = 0;
int index = 0;
struct timeval tv;
char szBuff[1024];
int read_len = 0;
int res = 0;
fd_set fs;
//先動態申請一個結構體大小空間
st_fd_def *p_st_fd = (st_fd_def*)arg;
if (p_st_fd == NULL)
{
printf("calloc memory error, %s,%d\n", __FUNCTION__, __LINE__);
return (void*)0;
}
//申請3個fd數組個數
p_st_fd->fd = (int*)calloc(3, sizeof(int));
if (p_st_fd->fd == NULL)
{
printf("calloc memory error, %s,%d\n", __FUNCTION__, __LINE__);
return (void*)0;
}
p_st_fd->count = 0;
p_st_fd->max_count =3;
max_fd = get_max_fd(p_st_fd, &fs);
tv.tv_sec = 2;
tv.tv_usec = 0;
FD_ZERO(&fs);
while(res = select(max_fd+1, &fs, NULL, NULL, &tv) >=0)
{
//大於0,說明有多少個讀文件描述符狀態發送了變化
if (res > 0)
{
//遍歷文件描述符數組,根據FD_ISSET查看哪些描述符狀態發生了變化
for (index = 0; index<p_st_fd->count; index++)
{
if (FD_ISSET(p_st_fd->fd[index], &fs))
{
printf("max_fd=========%d\n", max_fd);
memset(szBuff, 0x00, sizeof(szBuff));
read_len = read(p_st_fd->fd[index], szBuff, sizeof(szBuff));
if (read_len <= 0)
{
printf("read socket[%d] closed\n", p_st_fd->fd[index]);
//當讀取錯誤或客戶端斷開時,從描述符集中刪除指定描述符
FD_CLR(p_st_fd->fd[index], &fs);
close(p_st_fd->fd[index]);
//刪除描述符數組中的指定描述符
remove_fd(p_st_fd, p_st_fd->fd[index]);
continue;
}
printf("server read socket[%d] data is [%s]\n", p_st_fd->fd[index], szBuff);
write(p_st_fd->fd[index], szBuff, strlen(szBuff));
}
}
}
//由於服務端不斷在接收新的連接,所以需要重複獲取最大描述符
max_fd = get_max_fd(p_st_fd, &fs);
}
}
int main(int argc, char* argv[])
{
int client_fd;
int server_fd;
int res = 0;
int opt = 1;
socklen_t clientaddr_len;
struct sockaddr_in serveraddr;
struct sockaddr_in clientaddr;
st_fd_def st;
pthread_t tid;
if (argc < 2)
{
printf("usgae: %s port\n", argv[0]);
return -1;
}
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0)
{
perror("socket error");
return -1;
}
/* 端口重複利用 */
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (void*)&opt, sizeof(opt));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[1]));
serveraddr.sin_addr.s_addr = INADDR_ANY;
res = bind(server_fd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr));
if (res != 0)
{
perror("bind error");
return -1;
}
res = listen(server_fd, 10);
if (res != 0)
{
perror("listen error");
return -1;
}
clientaddr_len = sizeof(struct sockaddr_in);
/*
線程函數中調用select,主線程用於接收客戶端請求
*/
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, NULL, thread_fun, (void*)&st);
pthread_attr_destroy(&attr);
while(1)
{
client_fd = accept(server_fd, (struct sockaddr*)&clientaddr, &clientaddr_len);
if (client_fd < 0)
{
perror("client_fd error");
return -1;
}
// 將接收的client_fd描述符放置到描述符數組中
add_fd(&st, client_fd);
}
close(server_fd);
return 0;
}
編譯:gcc server_select.c -o server_select -lpthread
./server_select 8888
結果:
fd=4, count=1, max_count=3
fd=5, count=2, max_count=3
fd=6, count=3, max_count=3
fd=7, count=4, max_count=6
max_fd=7
server read socket[4] data is [aaa]
max_fd=7
server read socket[5] data is [aaa]
max_fd=7
server read socket[6] data is [aaa]
max_fd=7
server read socket[7] data is [aaa]
max_fd=7
read socket[4] closed
max_fd=7
read socket[5] closed
max_fd=7
read socket[6] closed
max_fd=7
read socket[7] closed