Linux C TCP服务器端-select案例

简介: 本文主要介绍了linux下Select的TCP通信流程,实现了客户端和服务器的通信,主要实现了消息的回发,即服务器将消息原封不动的回发给客户端。

前言

本文主要介绍了linux下Select的TCP通信流程,实现了客户端和服务器的通信,主要实现了消息的回发,即服务器将消息原封不动的回发给客户端。

这里主要介绍服务端代码,关于客户端代码请参考客户端代码和socket的基本使用


服务器端代码

#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define MAXLNE  4096
int main(int argc, char **argv) 
{
    int listenfd, connfd, n;
    struct sockaddr_in servaddr;
    char buff[MAXLNE];
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(9999);
    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
        printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    //listen接口之后会一直监听客户端的连接,每次客户端连接,都会和其创建连接(三次连接时内核完成的,不是由应用程序去控制的)
  //三次握手不发生在任何API中,协议栈本身被动完成的。
    if (listen(listenfd, 10) == -1) {
        printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    //select模型相比于一请求一线程而言
  //一个select最多支持1024 socket,如果开多个线程,那么是能支持大量socket的。
    //select不能做到100w级别的fd,select函数本身有copy全部监控的socket,循环这些
  //全部监控的socket存在一定的效率问题
  // fd_set,每个socket对应一个bit位,bit位为1,代表该socket发生了事件。
  //rfds:是需要监控读的集合(从用户copy到内核)
  //rset:是发生了读事件的socket的集合(从内核copy到用户)
  fd_set rfds, rset, wfds, wset;
  FD_ZERO(&rfds);//清空bit位
  FD_SET(listenfd, &rfds);//设置监听的socket的bit位
  FD_ZERO(&wfds);
    //比如listenfd为100,那么select就需要从第一位bit监控到101位的bit
  int max_fd = listenfd;//select第一个参数为监听socket的最大值+1
  while (1) {
  rset = rfds;
  wset = wfds;
        //一个专门的服务员来给所有的客户端服务,可以知道到底是哪些客户端发生了相应的事件(比如发送了数据给服务器)
  //如果最后一个参数为NULL,那么是阻塞的,一直到有事件发生(nready会大于0)
  int nready = select(max_fd+1, &rset, &wset, NULL, NULL);
  if (FD_ISSET(listenfd, &rset)) { //listenfd监听到了客户端连接
    struct sockaddr_in client;
      socklen_t len = sizeof(client);
      if ((connfd = accept(listenfd, (struct sockaddr *)&client, &len)) == -1) {
          printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
          return 0;
      }
    FD_SET(connfd, &rfds);//加入到读的集合中,代表要监听某客户端的读(recv)事件
    //如果客户端没有回收,connfd比max_fd大,但是如果客户端回收了fd,那么数字会变小,因此需要判断当前的connfd是否大于max_fd  
    if (connfd > max_fd) max_fd = connfd;//重新设置最大的fd
    if (--nready == 0) continue;//只有一个客户端的连接
  }
  int i = 0;
  //注意0-标志输入,1-标志输出,2-错误,然后是listenfd,
  //然后每次客户端连接后,connfd会依次增大。回收后connfd会变小,但是无论如何都比listenfd大
  //因此这里检测的是所有客户端是否有客户端与服务器进行了通信
  for (i = listenfd+1;i <= max_fd;i ++) {
    if (FD_ISSET(i, &rset)) { //检测某个客户端是否已经准备好了数据或者关闭(正常关闭也会设置读数据)
    n = recv(i, buff, MAXLNE, 0);//
          if (n > 0) { //收到n个字节的数据
              buff[n] = '\0';
              printf("recv msg from client: %s\n", buff);
      设置可以往该客户端socket发送数据
      //有兴趣的朋友可以去为每个fd封装一个发送缓冲和接收缓冲,设置写标志后
      //然后再下面的else if (FD_ISSET(i, &wset))发送数据给客户端
      FD_SET(i, &wfds);
      send(i, buff, n, 0);//直接将接收到的数据发送给客户端
          } else if (n == 0) { //收到的字节为0,代表该客户端正常关闭了
      FD_CLR(i, &rfds);//清空该客户端的读事件
      //printf("disconnect\n");
              close(i);
          }
    if (--nready == 0) break;//没有客户端的读事件了
    } 
    else if (FD_ISSET(i, &wset)) {//某个客户端写事件已就绪
    // send(i, buff, n, 0);  //将前面接收到数据发送给客户端
    // FD_SET(i, &rfds);//
    }
  }
  } 
    close(listenfd);
    return 0;
}

说明

代码主要说明了select的基本用法,一般服务器使用select来实现多路复用的代码都跟其类似,不过在实际的开发中,一般select是和定时器事件结合起来的,因为在一般的网络中,包括定时器事件和网络I/O事件,在一次消息循环中需要同时处理定时器事件和网络I/O事件。,有兴趣的朋友可以参考live555的消息循环的相关代码。


相关文章
|
12天前
|
存储 数据挖掘 Linux
服务器数据恢复—服务器重装系统导致原分区丢失的数据恢复案例
服务器数据恢复环境&故障: 磁盘柜中有一组通过RAID卡创建的RAID5阵列,分配一个LUN,服务器上层安装Linux操作系统。操作系统层面划分sdc1和sdc2两个分区。通过LVM扩容的方式将sdc1分区加入到了root_lv中;sdc2分区格式化为XFS文件系统。 服务器重装操作系统后,磁盘分区改变,sdc2分区丢失,无法访问。
服务器数据恢复—服务器重装系统导致原分区丢失的数据恢复案例
|
3天前
|
存储 运维 数据挖掘
服务器数据恢复—EMC存储raid5阵列故障导致存储瘫痪的数据恢复案例
服务器存储数据恢复环境: 北京某企业一台EMC FCAX-4存储上搭建一组由12块成员盘的raid5磁盘阵列,其中包括2块热备盘。 服务器存储故障: raid5阵列中两块硬盘离线,热备盘只有一块成功激活,raid瘫痪,上层LUN无法使用,存储崩溃。
|
4天前
|
存储 运维 数据挖掘
服务器数据恢复—RAID5阵列两块盘指示灯亮黄色的数据恢复案例
服务器存储数据恢复环境: 某品牌P2000存储,存储中有一组由8块硬盘(包含一块热备盘)组建的raid5阵列。上层部署VMWARE ESX虚拟化平台。 服务器存储故障: 存储在运行过程中有两块硬盘指示灯亮黄色。经过运维人员的初步检测,判断指示灯亮黄色的硬盘出现物理故障,硬盘故障表现为:序列号无法读取,在SAS扩展卡上无法识别。
服务器数据恢复—RAID5阵列两块盘指示灯亮黄色的数据恢复案例
|
7天前
|
数据库
服务器数据恢复—同品牌不同系列服务器raid5阵列数据恢复案例
服务器数据恢复环境: 一台某品牌LH6000系列服务器,通过NetRaid阵列卡将4块硬盘组建为一组RAID5磁盘阵列。操作系统都为Window server,数据库是SQLServer。 服务器故障: LH6000系列服务器故障如下: 一块硬盘指示灯亮红灯,服务器还在正常运行,但没过多久,服务器就无法正常运行,这时另一块硬盘的指示灯变成红色。
|
10天前
|
存储 运维 Oracle
服务器数据恢复—存储中硬盘出现坏道离线导致存储不可用的数据恢复案例
服务器存储数据恢复环境: 某单位一台某品牌DS5300存储,1个机头+4个扩展柜,50块硬盘组建2组RAID5磁盘阵列(一组raid5阵列有27块成员盘,存放Oracle数据库文件;另外一组raid5阵列有23块成员盘)。存储系统上层一共分了11个卷。 服务器存储故障: 磁盘故障导致27盘RAID5阵列崩溃,存储不可用,该存储设备已经过保。
|
10天前
|
网络协议 网络架构
【网络编程入门】TCP与UDP通信实战:从零构建服务器与客户端对话(附简易源码,新手友好!)
在了解他们之前我们首先要知道网络模型,它分为两种,一种是OSI,一种是TCP/IP,当然他们的模型图是不同的,如下
|
10天前
|
SQL 自然语言处理 网络协议
【Linux开发实战指南】基于TCP、进程数据结构与SQL数据库:构建在线云词典系统(含注册、登录、查询、历史记录管理功能及源码分享)
TCP(Transmission Control Protocol)连接是互联网上最常用的一种面向连接、可靠的、基于字节流的传输层通信协议。建立TCP连接需要经过著名的“三次握手”过程: 1. SYN(同步序列编号):客户端发送一个SYN包给服务器,并进入SYN_SEND状态,等待服务器确认。 2. SYN-ACK:服务器收到SYN包后,回应一个SYN-ACK(SYN+ACKnowledgment)包,告诉客户端其接收到了请求,并同意建立连接,此时服务器进入SYN_RECV状态。 3. ACK(确认字符):客户端收到服务器的SYN-ACK包后,发送一个ACK包给服务器,确认收到了服务器的确
|
11天前
|
存储 运维 Oracle
服务器数据恢复—DS4700存储中raid出现故障的数据恢复案例
一台某品牌DS4700存储中有14块硬盘组建raid,存放的是oracle数据库。存储中有两块硬盘的指示灯亮黄色,raid崩溃,卷无法挂载,业务全部瘫痪。
|
10天前
|
网络协议 Linux
云服务器内部端口占用,9090端口已经存在了,如何关闭,Linux查询端口,查看端口,端口查询,关闭端口写法-netstat -tuln,​fuser -k 3306/tcp​
云服务器内部端口占用,9090端口已经存在了,如何关闭,Linux查询端口,查看端口,端口查询,关闭端口写法-netstat -tuln,​fuser -k 3306/tcp​
|
10天前
|
大数据 Linux 程序员
软件开发常见流程之服务器+Linux部署项目,会用服务器+Linux部署项目资料
软件开发常见流程之服务器+Linux部署项目,会用服务器+Linux部署项目资料