from:
https://blog.csdn.net/analogous_love/article/category/7257412 struct sockaddr_in bindaddr;
bindaddr.sin_family = AF_INET;
bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bindaddr.sin_port = htons(3000);
if (bind(listenfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) == -1)
{
std::cout << "bind listen socket error." << std::endl;
return -1;
}

 上面代码的细节    INADDR_ANY 宏 就是 ‘0.0.0.0’     假设我们在一台机器上开发一个服务器程序,使用 bind 函数时,我们有多个ip 地址可以选择。首先,这台机器对外访问的ip地址是 120.55.94.78,这台机器在当前局域网的地址是 192.168.1.104;同时这台机器有本地回环地址127.0.0.1。

如果只想让本机 访问 bind 函数中的地址 可以使用127.0.0.1 ; 若服务器只想被局域网内部的机器访问, 地址可以使用 192.168.1.104; 若希望被公网访问 那么就可以使用 0.0.0.0  or  INADDR_ANY   当年部署到阿里云的时候遇到这个坑人的细节  解决了 好久 才搞定的。    

上面是  bind函数在serv 端口的调用情况   问题分析

如果在客户端  调用   bind 函数  会出现 上面问题呢?

发现三个 client 进程使用的端口号仍然是系统随机分配的,也就是说绑定 0 号端口和没有绑定效果是一样的。

情形三:客户端绑定一个固定端口

我们这里使用 20000 端口,当然读者可以根据自己的喜好选择,只要保证所选择的端口号当前没有被其他程序占用即可,服务器代码保持不变,客户端绑定代码中的端口号从 0 改成 20000。

发现 client 进程确实使用 20000 号端口连接到 server 进程上去了。这个时候如果我们再开启一个 client 进程,我们猜想由于端口号 20000 已经被占用,新启动的 client 会由于调用 bind 函数出错而退出

select api使用

Linux 平台下的 select 函数

select 函数的作用是检测一组 socket 中某个或某几个是否有“事件”就绪,这里的“事件”一般分为如下三类:

读事件就绪:

1 socket 内核中,接收缓冲区中的字节数大于等于低水位标记 SO_RCVLOWAT,此时调用 recv 或 read 函数可以无阻塞的读该文件描述符, 并且返回值大于0;
2 TCP 连接的对端关闭连接,此时调用 recv 或 read 函数对该 socket 读,则返回 0;
3 侦听 socket 上有新的连接请求;
4 socket 上有未处理的错误。

写事件就绪:

1 socket 内核中,发送缓冲区中的可用字节数(发送缓冲区的空闲位置大⼩) 大于等于低水位标记 SO_SNDLOWAT,此时可以无阻塞的写, 并且返回值大于0;
3 socket 的写操作被关闭(调用了 close 或者 shutdown 函数)     ( 对一个写操作被关闭的 socket 进行写操作, 会触发 SIGPIPE 信号);
4 socket 使⽤非阻塞 connect 连接成功或失败之后;

异常事件就绪

socket 上收到带外数据。

=================================================================================

select  使用 流程

注意一些细节问题 就是  

select 函数调用前后会修改 readfds、writefds 和 exceptfds 这三个集合中的内容(如果有的话),所以如果您想下次调用 select 复用这个变量,记得在下次调用前再次调用 select 前先使用 FD_ZERO 将集合清零,然后调用 FD_SET 将需要检测事件的 fd 再次添加进去。  (这里也是   select的三大缺点之一吧 相对于  epoll  模型   当然咯最主要的 不是这里  缺点  是在 while循环中的for循环检测  ,还有一个就是 内核和用户态进行  copy)

/**
* select函数示例,server端, select_server.cpp
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
#include <string.h>
#include <sys/time.h>
#include <vector>
#include <errno.h> //自定义代表无效fd的值
#define INVALID_FD -1 int main(int argc, char* argv[])
{
//创建一个侦听socket
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd == -1)
{
std::cout << "create listen socket error." << std::endl;
return -1;
} //初始化服务器地址
struct sockaddr_in bindaddr;
bindaddr.sin_family = AF_INET;
bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bindaddr.sin_port = htons(3000);
if (bind(listenfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) == -1)
{
std::cout << "bind listen socket error." << std::endl;
close(listenfd);
return -1;
} //启动侦听
if (listen(listenfd, SOMAXCONN) == -1)
{
std::cout << "listen error." << std::endl;
close(listenfd);
return -1;
} //存储客户端socket的数组
std::vector<int> clientfds;
int maxfd = listenfd; while (true)
{

fd_set readset;
FD_ZERO(&readset); //将侦听socket加入到待检测的可读事件中去
FD_SET(listenfd, &readset); //将客户端fd加入到待检测的可读事件中去
int clientfdslength = clientfds.size();
for (int i = 0; i < clientfdslength; ++i)
{
if (clientfds[i] != INVALID_FD)
{
FD_SET(clientfds[i], &readset);
}
}
timeval tm;
tm.tv_sec = 1;
tm.tv_usec = 0;

//暂且只检测可读事件,不检测可写和异常事件
int ret = select(maxfd + 1, &readset, NULL, NULL, &tm);//细节就是 每次select调用结束后 可读 可写 异常 事件集合都需要从新设置 包括第5个超时参数 都要从新设置
if (ret == -1)
{
//出错,退出程序。
if (errno != EINTR)
break;
}
else if (ret == 0)
{
//select 函数超时,下次继续
continue;
}
else
{
//检测到某个socket有事件
if (FD_ISSET(listenfd, &readset))
{
//侦听socket的可读事件,则表明有新的连接到来
struct sockaddr_in clientaddr;
socklen_t clientaddrlen = sizeof(clientaddr);
//4. 接受客户端连接
int clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddrlen);
if (clientfd == -1)
{
//接受连接出错,退出程序
break;
} //只接受连接,不调用recv收取任何数据
std:: cout << "accept a client connection, fd: " << clientfd << std::endl;
clientfds.push_back(clientfd);
//记录一下最新的最大fd值,以便作为下一轮循环中select的第一个参数
if (clientfd > maxfd)
maxfd = clientfd;
}
else
{
//假设对端发来的数据长度不超过63个字符
char recvbuf[64];
int clientfdslength = clientfds.size();
for (int i = 0; i < clientfdslength; ++i)//这里也是其效率低下的原因 相对于 epoll模型来说
{
if (clientfds[i] != -1 && FD_ISSET(clientfds[i], &readset))
{
memset(recvbuf, 0, sizeof(recvbuf));
//非侦听socket,则接收数据
int length = recv(clientfds[i], recvbuf, 64, 0);
if (length <= 0 && errno != EINTR)
{
//收取数据出错了
std::cout << "recv data error, clientfd: " << clientfds[i] << std::endl;
close(clientfds[i]);
//不直接删除该元素,将该位置的元素置位-1
clientfds[i] = INVALID_FD;
continue;
} std::cout << "clientfd: " << clientfds[i] << ", recv data: " << recvbuf << std::endl;
}
}
}
}
} //关闭所有客户端socket
int clientfdslength = clientfds.size();
for (int i = 0; i < clientfdslength; ++i)
{
if (clientfds[i] != INVALID_FD)
{
close(clientfds[i]);
}
} //关闭侦听socket
close(listenfd); return 0;
}

  

上面的demo 对select调用  第五个超时参数的设置需要注意一点细节就是  传入参数为  3种情况

1.非0值的                  就是检测 等待到相对应的时间就返回 

2.传入为NULL          参数设置为 NULL,则 select 函数会一直阻塞下去,直到我们需要的事件触发

3. 传入为0的             监测对应的检测事件 检测结束就返回   并不等待固定时间

对上面的总结  select 使用的4个注意事项

Linux select 函数的缺点也是显而易见的:

1 每次调用 select 函数,都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 较多时会很大

2 同时每次调用 select 函数都需要在内核遍历传递进来的所有 fd,这个开销在 fd 较多时也很大

3 单个进程能够监视的文件描述符的数量存在最大限制,在 Linux 上一般为 1024,可以通过修改宏定义然后重新编译内核的方式提升这一限制,这样非常麻烦而且效率低下

4  select 函数在每次调用之前都要对传入参数进行重新设定,这样做比较麻烦而且会降低性能

网络通信基础重难点解析 05 :socket 的阻塞模式和非阻塞模式

网络编程api bind函数细节 select 细节的更多相关文章

  1. socket网络编程快速上手(二)——细节问题(4)

    5.慢系统调用及EINTR 还记得前面readn和writen函数么?里面有个EINTR,现在就来谈谈这个,这个很重要. Linux世界有个叫信号的东西,感觉他就像一位隐士,很少遇到他,而他又无处不在 ...

  2. socket网络编程快速上手(二)——细节问题(5)(完结篇)

    6.Connect的使用方式 前面提到,connect发生EINTR错误时,是不能重新启动的.那怎么办呢,是关闭套接字还是直接退出进程呢?如果EINTR前,三次握手已经发起,我们当然希望链路就此已经建 ...

  3. socket网络编程快速上手(二)——细节问题(1)

    三.细节问题一个也不能少 Socket编程说简单也简单,程序很容易就能跑起来,说麻烦还真是麻烦,程序动不动就出问题.记得刚开始写网络代码的时候,那真是令人抓狂的经历,问题一个套一个,一会服务器起不来了 ...

  4. UNIX网络编程——I/O复用:select和poll函数

    我们看到TCP客户同时处理两个输入:标准输入和TCP套接字.我们遇到的问题是就在客户阻塞于(标准输入上)fgets调用,服务器进程会被杀死.服务器TCP虽然正确的给客户TCP发送了一个FIN,但是既然 ...

  5. C# 通过豆瓣网络编程API获取图书信息

    这篇文章主要是关于如何通过豆瓣API获取信息的书籍,起初,我看到了原来的想法的内容是"C# 网络编程之网页简单下载实现"中通过HttpWebResponse类下载源代码,再通过正則 ...

  6. Linux 网络编程 入门-常用函数

    网络连接无外乎服务器和客户端两方面的编程. 对于服务器大致的流程是:1---调用socket函数创建套接字 2---调用bind函数分配IP地址和端口号 3---调用listsen函数将套接字转为可接 ...

  7. 网络通讯中 bind函数的作用

    面向连接的网络应用程序分为客户端和服务器端.服务器端的执行流程一般为4步,客户端程序相对简单,一般需要两个步骤. 服务器端执行流程4步如下: (1)调用socket函数,建立一个套接字,该套接字用于接 ...

  8. Windows网络编程系列教程之四:Select模型

    讲一下套接字模式和套接字I/O模型的区别.先说明一下,只针对Winsock,如果你要骨头里挑鸡蛋把UNIX下的套接字概念来往这里套,那就不关我的事. 套接字模式:阻塞套接字和非阻塞套接字.或者叫同步套 ...

  9. [转载]Windows网络编程系列教程之四:Select模型

    原文:http://www.51see.com/asp/bbs/public/bp_show.asp?t_id=200308131152297103 讲一下套接字模式和套接字I/O模型的区别.先说明一 ...

随机推荐

  1. HTTP文件上传插件开发文档-JSP

    版权所有 2009-2016 荆门泽优软件有限公司 保留所有权利 官方网站:http://www.ncmem.com/ 产品首页:http://www.ncmem.com/webplug/http-u ...

  2. Java之集合框架vector类设计原理

  3. Dennis Gabor与全息摄影

    Dennis Gabor:1900年6月5日出生在匈牙利首都布达佩斯,1971年诺贝尔物理学奖授予英国伦敦帝国科技学院的匈牙利裔物理学家伽博(Dennis Gabor,1900-1979),以表彰他发 ...

  4. POJ3259 Wormholes(SPFA判断负环)

    Description While exploring his many farms, Farmer John has discovered a number of amazing wormholes ...

  5. Sharepoint2013搜索学习笔记之自定义结果显示模板(九)

    搜索结果通过套用定义好的显示模板来展示结果,显示模板由js和html组成,我们可以通过修改显示模板,然后将修改好的显示模板跟搜索结果绑定起来,来修改搜索结果的显示效果,例子如下图: 修改前 修改后 第 ...

  6. WPF 控件库——可拖动选项卡的TabControl

    WPF 控件库系列博文地址: WPF 控件库——仿制Chrome的ColorPicker WPF 控件库——仿制Windows10的进度条 WPF 控件库——轮播控件 WPF 控件库——带有惯性的Sc ...

  7. CentOS 用户/组与权限

    useradd:添加用户 useradd abc,默认添加一个abc组 vipw:查看系统中用户 groupadd:添加组groupadd ccna vigr:查看系统中的组 gpasswd:将用户a ...

  8. LOJ#10172. 「一本通 5.4 练习 1」涂抹果酱

    题目链接:https://loj.ac/problem/10172 题目描述 Tyvj 两周年庆典要到了,Sam 想为 Tyvj 做一个大蛋糕.蛋糕俯视图是一个 N×MN×MN×M 的矩形,它被划分成 ...

  9. Promise超时重新请求

    export default function (promiseProducer, params, interval, attemptTimes) { interval = typeof interv ...

  10. Django 使用第三方服务发送电子邮件

    在 Django 网站中使用 mailgun 的邮件收发服务. 1.在 mailgun 官网上注册个账号(免费,免费账号每个月有10000条收发邮件的服务,对我来说已经完全够用了),注册完成后界面如图 ...