网络编程3--多进程/多线程编程 IO模型

select系统调用第一个参数需要+1
先说明一下, 在Windows中, 并不要求select函数的第一个参数总应该是fdmax + 1(在Windows下, 给定-1就行), 那linux中为什么又是呢?
       这就涉及到linux select第一个参数的函数: 待测试的描述集的总个数。 但要注意, 待测试的描述集总是从012...开始的。 所以, 假如你要检测的描述符为8910, 那么系统实际也要监测0123456,  7,  此时真正待测试的描述符的个数为11个, 也就是max8910+ 1
       有两点要注意:
       1.  如果你要检测描述符8, 9, 10, 但是你把select的第一个参数定为8, 实际上只检测07, 所以select不会感知到8, 9, 10描述符的变化。
       2.  如果你要检测描述符8, 9, 10, 且你把select的第一个参数定为11, 实际上会检测0-10, 但是, 如果你不把描述如0 set到描述符中, 那么select也不会感知到0描述符的变化。
       所以, select感知到描述符变化的必要条件是, 第一个参数要合理, 比如定义为fdmax+1,  且把需要检测的描述符set到描述集中。
4种IO模型
信号驱动式IO:开启套接字的信号驱动IO功能,通过sigaction系统调用安装一个信号处理函数,当数据报准备好时,内核为进程产生SIGIO信号,在信号处理函数中调用recvfrom读取数据报

select系统调用数据准备好的条件:
1.套接字接受缓冲区中的数据字节数大于等于套接字接受缓冲区低水位标记线
2.该链接的读半关闭,接收到FIN信号,读将不阻塞而是返回0
3.套接字是一个监听套接字,且已完成的连接数不为0,accept不会阻塞
4.套接字上的错误处理


标准程序实例:
void str_cli(FILE *fp, int sockfd){
    int maxfdp1;
    fd_set rset;
    char sendline[MAXLINE], recvline[MAXLINE];
    FD_ZERO(&rset);
    for(;;){
        FD_SET(fileno(fp), &rset);
        FD_SET(sockfd, &rset);
        maxfdp1 = max(fileno(fp), sockfd) + 1;
        Select(maxfdp1, &rset, NULL, NULL, NULL);
        if(FD_ISSET(sockfd, &rset)){
                if(Readline(sockfd, recvline, MAXLINE) == 0){
                exit(1);
        }

        }
    }
}
/*多线程并发服务器实例*/
/*该代码不涉及线程安全函数*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <stdlib.h>

#define PORT 1234
#define BACKLOG 5
#define MAXDATASIZE 1000

void process_cli(int connfd, struct sockaddr_in client);
void *function(void *arg);

struct ARG{
  int connfd;
  struct sockaddr_in client;
};

int main()
{
  int listenfd, connfd;
  pthread_t  tid;
  struct ARG *arg;
  struct sockaddr_in server;
  struct sockaddr_in client;
  socklen_t len;

  if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
    perror("Creating socket failed.\n");
    exit(1);
  }

  int opt = SO_REUSEADDR;
  setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

  bzero(&server,sizeof(server));
  server.sin_family = AF_INET;
  server.sin_port = htons(PORT);
  server.sin_addr.s_addr = htonl(INADDR_ANY);

  if(bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -1){
    perror("bind() error.\n");
    exit(1);
  }

  if(listen(listenfd, BACKLOG) == -1){
    perror("listen() error.\n");
    exit(1);
  }

  len = sizeof(client);
  while(1){
    if((connfd = accept(listenfd, (struct sockaddr *)&client, &len)) == -1){
      perror("accept() error.\n");
      exit(1);
    }
    arg = (struct ARG *)malloc(sizeof(struct ARG));
    arg->connfd = connfd;
    memcpy((void *)&arg->client, &client, sizeof(client));
    //创建线程,每个线程调用function函数,参数为arg
    if(pthread_create(&tid, NULL, function, (void *)arg)){
      perror("pthread_create() error.\n");
      exit(1);
    }
  }
  close(listenfd);
}

//处理客户请求函数,将客户传递过来的字符串逆序返回
void process_cli(int connfd, struct sockaddr_in client)
{
  int num;
  char recvbuf[MAXDATASIZE], sendbuf[MAXDATASIZE], cli_name[MAXDATASIZE];
  printf("You got a connection from %s.\n",inet_ntoa(client.sin_addr));

  num = recv(connfd, cli_name, MAXDATASIZE, 0);//返回字节数num,非字符串长度
  if(num == 0){
    close(connfd);
    printf("Client disconnected.\n");
    return;
  }
  cli_name[num - 1] = '\0';
  printf("Client's name is %s.\n", cli_name);

  while(num = recv(connfd, recvbuf, MAXDATASIZE, 0)){//num为字节数,num-1为字符串长度
    recvbuf[num - 1] = '\0';
    printf("Received client(%s) message:%s\n", cli_name, recvbuf);
    int i;
    for(i = 0; i < num - 1; ++i){//将字符串逆序返回
      sendbuf[num - 2 - i] = recvbuf[i];
    }
    sendbuf[num - 1] = '\0';
    send(connfd, sendbuf, strlen(sendbuf), 0);
  }
  close(connfd);
}

void *function(void *arg)
{
  struct ARG *info;
  info = (struct ARG *)arg;
  process_cli(info->connfd, info->client);//参数分别为:文件描述符、sockaddr_in
  free(arg);//释放内存
  pthread_exit(NULL);//退出线程
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define PORT 1234
#define MAXDATASIZE 100

void process(FILE *fp, int sockfd);
char *getMessage(char *sendline, int len, FILE *fp);

int main(int argc, char *argv[])
{
  int sockfd;
  struct hostent *he;
  struct sockaddr_in server;
  if(argc != 2){
    printf("Usage: %s <IP Address>\n", argv[0]);
    exit(1);
  }
  if(((he = gethostbyname(argv[1])) == NULL)){
    printf("gethostbyname() error.\n");
    exit(1);
  }
  if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
    printf("socket() error.\n");
    exit(1);
  }

  bzero(&server, sizeof(server));
  server.sin_family = AF_INET;
  server.sin_port = htons(PORT);
  server.sin_addr = *((struct in_addr *)he->h_addr);

  if(connect(sockfd, (struct sockaddr *)&server, sizeof(server)) == -1){
    printf("connect() error.\n");
    exit(1);
  }

  process(stdin, sockfd);
  close(sockfd);

}

void process(FILE *fp, int sockfd){
  char sendline[MAXDATASIZE], recvline[MAXDATASIZE];
  int num;
  printf("Connected to server.\n");
  printf("Input client's name:");
  if( fgets(sendline, MAXDATASIZE, fp) == NULL){
    printf("\nExit.\n");
    return ;
  }
  send(sockfd, sendline, strlen(sendline), 0);

  while(getMessage(sendline, MAXDATASIZE, fp) != NULL){
    send(sockfd, sendline, strlen(sendline), 0);
    if((num = recv(sockfd, recvline, MAXDATASIZE, 0)) == 0){
      printf("Server terminated.\n");
      return ;
    }
    recvline[num] = '\0';
    printf("Server Message:%s\n", recvline);
  }
}

char *getMessage(char *sendline, int len, FILE *fp){
  printf("Input string to server:");
  return (fgets(sendline, MAXDATASIZE, fp));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值