小试Linux下的select

本文介绍了一种在Linux环境下利用socket的select机制实现多连接数据接收的方法。通过具体实例展示了如何使用两个线程来分别处理连接请求和数据交互,特别强调了select在连接数不多的情况下的高效性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       这篇文章主要是关于在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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值