这篇文章主要是关于在linux下socket的select的。
最近了解了一下select,感觉还不错,他既使socket处于阻塞状态下,有可以实现多连接实时的数据接收。在连接数不大的时候用起来非常理想,但是,连接数超过64之后,就不太好用了,因为,select在一个线程中只支持64个连接,如果将select支持的连接数改大些的话,系统性能可能会大幅下降。(网上看到的观点,并没亲自试验过)
关于select的用法,我在这就不叙述了,网上的教程,文档太多了。不废话了,直接上代码。
下面是一个server端和client端代码。(这个例子中,我让server最大只能接受64个连接)
server.c(服务器用了两个线程,一个线程阻塞接受连接,另一个线程处理数据收发)
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <sys/time.h>
#include <memory.h>
#define SERVER_PORT 8090
#define BACKLOG 10
#define MAX_DATA_SIZE 1024
void *sub_thread();
typedef struct c_fd
{
int cli_fd;
struct c_fd *next;
}struct_client_fd;
struct_client_fd *head_client_fd;
int main(int argc, char *argv[])
{
int sock_fd, client_fd;
socklen_t sin_len;
pthread_t thread_id;
struct_client_fd *delete_p1, *delete_p2, *short_p1, *short_p2, *short_p3;
struct sockaddr_in ser_addr;
struct sockaddr_in remote_addr;
int i = 0;
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(SERVER_PORT);
ser_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(ser_addr.sin_zero), 8);
short_p2 = (struct_client_fd *)malloc(sizeof(struct_client_fd));
head_client_fd = (struct_client_fd *)malloc(sizeof(struct_client_fd));
head_client_fd->cli_fd = -1;
head_client_fd->next = NULL;
if((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) ==-1)
{
perror("socket创建出错!!");
exit(1);
}
if(bind(sock_fd, (struct sockaddr *)&ser_addr, sizeof(struct sockaddr)) == -1)
{
perror("bind出错!!");
exit(1);
}
if(listen(sock_fd, BACKLOG) == -1)
{
perror("listen出错!!");
exit(1);
}
pthread_create(&thread_id, NULL, sub_thread, NULL);
sin_len = sizeof(struct sockaddr_in);
do
{
i = 0;
if((client_fd = accept(sock_fd, (struct sockaddr *)&remote_addr, &sin_len)) == -1)
{
perror("accept出错!!");
exit(1);
}
else
{
printf("accept a new connect!!\n");
short_p2->cli_fd = client_fd;
short_p2->next = NULL;
short_p1 = head_client_fd;
while(short_p1->next != NULL)
{
i++;
short_p3 = short_p1->next;
short_p1 = short_p3;
}
short_p1->next = short_p2;
i = i+1;
printf("现有连接数:%d\n", i);
short_p2 = (struct_client_fd *)malloc(sizeof(struct_client_fd));
}
}while(i < 64);
if(i >= 64)
{
char quit[4];
printf("连接数已达到上限(64)!!!\n");
do
{
printf("输入“quit”终止服务器!!\n");
scanf("%s",quit);
}while(strcmp(quit, "quit") != 0 && strcmp(quit, "QUIT") != 0);
}
close(sock_fd);
delete_p1 = head_client_fd;
delete_p2 = head_client_fd->next;
while(delete_p2 != NULL)
{
free(delete_p1);
delete_p1 = delete_p2;
delete_p2 = delete_p1->next;
}
return 0;
}
void *sub_thread()
{
struct_client_fd *client_fd_p1, *client_fd_p2, *send_p1, *send_p2;
int i = -1, j, data_len;
int client_fd_set[64];
fd_set r_fds;
struct timeval tim_v;
int ret_val, max_fd = -1;
char buf[MAX_DATA_SIZE];
while(1)
{
i = -1;
bzero(&client_fd_set, 64);
tim_v.tv_sec = 2;
tim_v.tv_usec = 0;
FD_ZERO(&r_fds);
FD_SET(0, &r_fds);
max_fd = 0;
client_fd_p1 = head_client_fd;
while(client_fd_p1 != NULL)
{
if(client_fd_p1->cli_fd > 0)
{
i++;
FD_SET(client_fd_p1->cli_fd, &r_fds);
client_fd_set[i] = client_fd_p1->cli_fd;
if(client_fd_set[i] > max_fd)
{
max_fd = client_fd_set[i];
}
}
client_fd_p2 = client_fd_p1;
client_fd_p1 = client_fd_p2->next;
}
ret_val = select(max_fd+1, &r_fds, NULL, NULL, &tim_v);
if(ret_val == -1)
{
printf("select出错!!\n");
exit(1);
}
for(j =0; j<=i; j++)
{
if(FD_ISSET(client_fd_set[j], &r_fds))
{
bzero(buf, MAX_DATA_SIZE);
data_len = recv(client_fd_set[j], buf, MAX_DATA_SIZE, 0);
printf("I recv a msg from : %d-------%s\n", client_fd_set[j], buf);
if(data_len > 0)
{
send_p1 = head_client_fd->next;
while(send_p1 != NULL)
{
send(send_p1->cli_fd, buf, strlen(buf), 0);
send_p2 = send_p1;
send_p1 = send_p2->next;
}
}
else if(data_len <= 0)
{
printf("I recv %d exit!\n", client_fd_set[j]);
close(client_fd_set[j]);
client_fd_p1 = head_client_fd->next;
client_fd_p2 = head_client_fd;
while(client_fd_p1 != NULL)
{
if(client_fd_p1->cli_fd == client_fd_set[j])
{
client_fd_p2->next = client_fd_p1->next;
free(client_fd_p1);
break;
}
else
{
client_fd_p2 = client_fd_p1;
client_fd_p1 = client_fd_p2->next;
}
}
}
}
}
}
}
下面是client.c(客户端也是有两个线程,一个线程用于发送数据,一个线程用于接收数据)
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>
#define SERVER_PORT 8090
#define MAX_DATA_SIZE 1024
void *sub_thread(void *argv)
{
int sock_fd, *p = (int*)argv;
sock_fd = *p;
char recv_buf[MAX_DATA_SIZE];
bzero(recv_buf, MAX_DATA_SIZE);
int recv_len;
while(1)
{
recv_len = recv(sock_fd, recv_buf, MAX_DATA_SIZE, 0);
if(recv_len > 0)
{
printf("recv: %s\n", recv_buf);
bzero(recv_buf, MAX_DATA_SIZE);
}
if(recv_len <= 0)
{
printf("recv出错!!\n");
close(sock_fd);
exit(1);
}
}
}
int main(int argc, char *argv[])
{
int sock_fd;
int send_len;
pthread_t thread_id;
char send_buf[MAX_DATA_SIZE];
bzero(send_buf, MAX_DATA_SIZE);
struct sockaddr_in server_addr;
//struct sockaddr_in client_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
bzero(&(server_addr.sin_zero), 8);
if((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket出错!!");
exit(1);
}
if(connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
perror("connect出错!!");
exit(1);
}
pthread_create(&thread_id, NULL, sub_thread, &sock_fd);
printf("输入要发送的信息:\n");
gets(send_buf);
while(strcmp(send_buf, "exit") != 0)
{
send_len = send(sock_fd, send_buf, sizeof(send_buf), 0);
if(send_len == 0)
{
printf("client quit!!\n");
exit(1);
}
if(send_len < 0)
{
printf("send失败!!\n");
}
printf("输入要发送的信息:\n");
bzero(send_buf, MAX_DATA_SIZE);
gets(send_buf);
}
close(sock_fd);
return 0;
}