select版的TCP通信

2023-05-16

        编写代码之前,大概先说一下利用select编写tcp的思路及select特点。

        select系统调用是用来让程序监视多个文件句柄的状态变化的,程序会停在select这里等待,直到被监视的句柄有一个或者多个发生了状态改变。

select函数为:int select(int nfds,fd_set *readfds,fd_set* writefds,异常文件描述符,时间长度)//默认时间长度异常事件描述符一般都为NULL

在select中提出四个宏来处理描述数组的方式:(fd为文件描述符集set为描述数组)

FD_SET(fd,fd_set* set):用来设置set中fd位

FD_ISSET(fd,fd_set* set):用来测试set中额相关fd位是否为真

FD_ZERO(fd,fd_set* set):用来清除set的全部位

FD_CLR(fd_set*set):用来清除set中相关的fd的位

当然select的模型最终的特点如下:(1)select下可监控的文件描述符的个数取决于系统的大小

(2)每次检测到一个新的fd,都要手动的将fd添加到set中,并且使用一个数据结构arrar来保存select监控集中的set

(3)select模型必须在设了select前循环array


在大致了解了select的特点之后,现在就来说一说select 版的tcp的编写思路:

1:搭建好基本的tcp的框架,在服务器端编写-->生成套接字(starup),绑定端口(bind),监听(listen)模块,接受客户端访问(accept)模块

2.客户端实现连接请求模块(connect)

3.定义一个数组arrary,初始值为-1,然后把步骤1 中生成的监听套接字(listen_sock)添加到该数组

4.编写select函数监听来自客户端的请求,若监听到客户端连接,就会生成一个new_sock套接字,那么当然把这个套接字添加到数组arrary中。当下次遍历数组时得到的是new_sock套接字,那它就被作为数据传输套接字进行数据接收。

5.在步骤4的基础上读取客户端放在缓冲区的内容。


好啦!以上大致就是编写思路的说明了,下面贴出代码:

服务器端:server.c

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>
#include<string.h>

#define port 8080
#define MAX_FD_NUM 20
int array_fd[MAX_FD_NUM];//for watch fd

int startup()
{
	int sock=socket(AF_INET,SOCK_STREAM,0);
	if(sock<0)
	{
		perror("socket");
		exit(1);
	}
	struct sockaddr_in local;
	local.sin_family=AF_INET;
	local.sin_port=htons(port);
	local.sin_addr.s_addr=htonl(INADDR_ANY);
	if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
	{
		perror("bind");
		exit(1);
	}
	if(listen(sock,5)<0)
	{
		perror("listen");
		exit(1);
	}
	return sock;
}
int main()
{
	int listen_sock=startup();

	struct sockaddr_in client;
        socklen_t len =sizeof(client);

	fd_set read_set;//READ 
	int max_fd=listen_sock;

	//init array
	int i=0;
	for(;i<MAX_FD_NUM;i++)
	{
	     array_fd[i]=-1;//set init key=-1
	}
	array_fd[0]=listen_sock;
	while(1)
	{
	  FD_ZERO(&read_set);//empty read_set
          for(i=0;i<MAX_FD_NUM;i++)
         {
           if(array_fd[i]>0)
          {
	      FD_SET(array_fd[i],&read_set);//set array_fd in red_set
              if(max_fd<array_fd[i])
		  {
			  max_fd=array_fd[i];//get max_fd
		  }
	  }
	 }
	  switch(select(max_fd+1,&read_set,NULL,NULL,NULL))
	  {
		case 0://timeout
			printf("timeout...");
			break;
		case -1://error
			perror("select");
                        break;
		default:
			for(i=0;i<MAX_FD_NUM;i++)
			{
				if(array_fd<0)
				{
					continue;
                }
				//new connect
				else if(array_fd[i]==listen_sock && FD_ISSET(array_fd[i],&read_set))
				{
					int new_sock=accept(array_fd[i],(struct sockaddr*)&client,&len);
					if(new_sock<0)
					{
						continue;
					}
					printf("get new connect: %d\n",new_sock);
					for(i=0;i<MAX_FD_NUM;i++)
					{
                                           if(array_fd[i]==-1)
					  {
						  array_fd[i]=new_sock;
						  break;
					  }
					}
					  if(i==MAX_FD_NUM)
					  {
						  close(new_sock);
					  }
				}
				else
				{
					for(i=1;i<MAX_FD_NUM;i++)
					{
						if(array_fd[i]>0 && FD_ISSET(array_fd[i],&read_set))
						{
							char buf[1024];
							memset(buf,'\0',sizeof(buf));
							ssize_t _size=read(array_fd[i],buf,sizeof(buf)-1);
							if(_size<0)
							{		
								printf("read failed");
								close(array_fd[i]);
							}
							else if(_size==0)
							{
								printf("client close...\n");
								close(array_fd[i]);
							}
							else
							{
								printf("client: %s\n",buf);
							}
						}
					}
				}
			}
			break;
	  }
	}

	close(listen_sock);
	return 0;
}

客户端:client.c

#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<errno.h>
int main()
{
   int read_fd=0;
   int write_fd=1;
   int max_fd=0;
   fd_set read_set;
   fd_set write_set;

   int sock=socket(AF_INET,SOCK_STREAM,0);
   if(sock<0)
   {
	   perror("socket");
	   exit(1);
   }
        struct sockaddr_in remote;
	remote.sin_family=AF_INET;
	remote.sin_port=htons(8080);
	remote.sin_addr.s_addr=inet_addr("192.168.198.128");//本机IP

	int new_sock=connect(sock,(struct sockaddr *)&remote,sizeof(remote));
	if(new_sock<0)
	{
		perror("connect");
		exit(1);
	}
	if(sock >read_fd)
	   max_fd=sock;
	else
		max_fd=read_fd;
	 while(1)
	 {
		 FD_ZERO(&read_set);//empty
		 FD_ZERO(&write_set);
		 FD_SET(read_fd,&read_set);//set
		 FD_SET(sock,&write_set);//

		 switch(select(max_fd+1,&read_set,&write_set,NULL,NULL))
		 {
			 case 0://timeout	
			 printf("timeout...\n");
				 break;
			case -1://error
			 printf("there are bug\n");
			 printf("error...\n");
				 break;
			default://data ready
				 {
				 if(FD_ISSET(read_fd,&read_set))//judge
				 {
					 char buf[1024];
						 ssize_t _size=read(read_fd,buf,sizeof(buf)-1);
						 if(_size>0)
						 {
						 buf[_size]='\0';
						 printf("echo:%s\n",buf);
						 }
				    	 if(FD_ISSET(sock,&write_set))
					     {
							 //printf("write successful\n");
						  send(sock,buf,strlen(buf),0);
                       // printf("%d\n",_size);
						 }
				
					 }
				 }
				 break;
		 }
	 }
	return 0;
}

Makefie文件:

.PHONY:all
all:server client
client:client.c
	gcc -o $@ $^
server:server.c
	gcc -o $@ $^
.PHONY:clean
clean:
	rm -r server client



















本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

select版的TCP通信 的相关文章

随机推荐

  • spdlog 封装为 DLL

    项目中需要快速写入巨量日志 xff0c 一开始用的是boost log xff0c 但遇到崩溃问题 xff0c 程序负载大了 xff0c 滚动日志时偶尔会崩溃 xff0c 总是报 xff1a boost filesystem error x
  • 驼峰命名法与下划线命名法之争

    窃以为 xff0c 驼峰命名开发效率更高 xff0c 原因如下 xff1a 下划线命名多输入一个字符 例如 xff1a set name 对比 setName xff0c 多输入一个下划线字符 xff0c 敲击键盘两次Shift 43 而驼
  • [PyQt] 在QLabel上用drawText实现文字滚动

    span class token keyword from span PyQt4 span class token punctuation span QtGui span class token keyword import span sp
  • [PyQt] PyQt4写的音乐播放器

    实现了 xff1a 播放歌曲 xff1b 上 下一首 xff1b 随机 循环 单曲循环 xff1b 批量添加 删除歌曲 xff1b 打开 存储播放列表 xff08 M3U格式 xff09 xff1b 添加到收藏 xff1b 单行 多行歌词桌
  • [PyQt] 使用.qrc 生成资源文件供程序中使用

    建立 images qrc文件 xff0c 里面保存了资源位置 xff1a span class hljs doctype lt DOCTYPE RCC gt span span class hljs tag lt span class h
  • Tcl/tk实例-工具栏和菜单-图片预览工具

    可以打开并查看图片 xff0c 点击工具栏上 Previous 和 Next 按钮来浏览 前 下 一张 仅仅是一个例子 xff0c 其它按钮和菜单未添加命令 package require Ttk package require Img p
  • Tcl/tk实例—使用tclkit工具将脚本打包成可执行文件(.exe)

    下载 tclkit exe 工具 xff0c 及 sdx kit 文件 复制一份tclkit exe xff0c 命名为tclkit2 exe 假设你的脚本文件为 xff1a app tcl Step1 命令行执行 tclkit exe s
  • C语言将十进制转换成十六进制

    include lt stdio h gt main char arr 50 61 34 34 char p 61 arr int i scanf 34 d 34 amp i 输入一个十进制的数 int result 61 0 while
  • 软件版本中的release,stable,alpha,beta,pre,snapshot

    转自 xff1a https www jianshu com p aefe0453d081 我们在下载软件会遇到诸如release stable alpha beta pre current eval rc snapshot等版本 xff0
  • kylin ubuntu20.04使用记录

    1 配置dns vim etc systemd resolved conf 修改 DNS 61 119 29 29 29 223 5 5 5 多个DNS地址使用空格分隔 2 配置samba sudo vim etc samba smb co
  • linux进程、线程状态 tomcat线程数 并发数查看

    1 linux进程查看 ps aux top USER PID CPU MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0 0 0 0 10368 116 Ss Jan16 4 57 init
  • 字符编码(三) 字节序、bit序、 有效位

    1 字节序 xff1a 一个 xff08 占多字节的 xff09 数据单元的字节顺序 Java中byte没有字节序问题 xff0c 其他都有字节序问题 不必考虑byte内部bit的细节 bit序 xff1a 一个字节内 xff0c bit的
  • STM32:串口发送

    1 main c代码如下 include stm32f10x h nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp Device header include Delay h include OLED
  • 太阳晒进屋里 太刺眼懒得起来关窗帘?做一个光感遥控吧

    在一个智能音响的交流群里 xff0c 有位群友提出了个想法 xff0c 家里太阳晒进屋里每次都要起来拉窗帘 xff0c 有没有什么办法可以让太阳晒进屋就关窗帘 xff0c 没太阳了又打开窗帘能 xff1f 后来这位群友找到我 xff0c 我
  • 用Python爬虫获取NBA球员的生涯数据

    NBA球迷往往对球员的各项数据以及对应的排名很感兴趣 xff0c 而basketball reference com这个网站的数据十分详尽 为方便浏览 xff0c 我在github建了一个项目 xff0c 借助该网站提供的数据来汇总某个球员
  • FreeRTOS 二值信号量

    参考 开发手册 二值信号量 简介 二值信号量通常用于互斥访问或同步 xff0c 二至信号量没有优先级继承 xff0c 更适合用于同步 xff08 任务与任务或任务与中断的同步 xff09 二值信号量其实就是一个只有一个队列项的队列 xff0
  • QGC地面站二次开发 环境搭建过程

    文章目录 将本机文件复制到安装的虚拟机系统中方法一方法二 ubuntu QT 安装过程问题一 xff1a 安装开始的时候 xff0c 显示磁盘容量不足 问题二 xff1a 现需解决 磁盘已成功扩展 您必须从客户机操作系统内部对磁盘重新进行分
  • c++应该怎样学习?c++服务器开发必备知识

    笔者从事软件开发工作5年 针对c 的特性 用途 整理的进阶式学习笔记 从浅入深地总结重点知识 本文旨在为c c 初学者 初中级开发者和意在转型c 服务器研发的同学们 对基础知识和进阶路线进行详细的整理 适合c 初学者 c 中高级开发岗的同学
  • Hadoop 安装与 HDFS 基础实践

    一 环境 xff08 1 xff09 操作系统 xff1a Linux Ubuntu 20 04 xff08 2 xff09 Hadoop 版本 xff1a 3 3 2 xff08 3 xff09 JDK 版本 xff1a 1 8 或者以上
  • select版的TCP通信

    编写代码之前 xff0c 大概先说一下利用select编写tcp的思路及select特点 select系统调用是用来让程序监视多个文件句柄的状态变化的 xff0c 程序会停在select这里等待 xff0c 直到被监视的句柄有一个或者多个发