1. Linuxsocket的简介

在linux支持select模式,poll模式,在内核2.6版本以后支持epoll模式;

epoll模式的优点:
A:支持进程打开的最大socket数据
B:IO效率不随FD数目增加而线性下降
C:使用mmap加速内核与用户空间的消息传递。
D:内核微调

2. socket的属性(例如:设置为非阻塞模式)

   1: /*设置socket为非阻塞模式
   2:  * window: ioctlsocket()
   3:  * linux: fcntl(), (头文件fcnt.h)
   4:  */
   5: int set_nonblock(int fd)
   6: {
   7:     int opts;
   8:     opts=fcntl(fd, F_GETFL); // 获得socket的属性
   9:     if (opts < 0)
  10:         return -1;
  11:  
  12:     opts = opts | O_NONBLOCK;
  13:     if(fcntl(fd, F_SETFL, opts) < 0) //设置socket的属性
  14:         return -1;
  15:  
  16:     return 0;
  17: }

3.socket的缓冲区的扩充

int nRecvBufferLen=2*1024*1024; //设置为2M的缓冲区
setsockopt(s,SOL_SOCKET,SO_RCVBUF, (const char*)&nRecvBufferLen, sizeof(nRecvBufferLen));

4 sockaddr 和 sockaddr_in 的关系和对比

   1: //sockaddr结构体:缺陷是sa_data把目标地址和端口信息混在一起了
   2: struct sockaddr {
   3:     unsigned short sa_family; //通信类型: AF_INET 或 UNIX
   4:     char sa_data[14]; // 14字节,包含套接字中的目标地址和端口信息 
   5: };
   6:   
   7: //sockaddr_in 结构体:解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中
   8: struct sockaddr_in {
   9:    short int sin_family;
  10:    unsigned short int sin_port; //端口(网络字节顺序)
  11:     struct in_addr  sin_addr;  //IP地址(网络字节顺序)
  12:      unsigned char sin_zero[8];
  13: } 
  14:  
  15: //注ip地址的结构
  16: struct in_addr { unsigned long s_addr; }

sockaddr 和 sockaddr_in的相互关系:
1.一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数
2.sockaddr_in用于socket定义和赋值
3.sockaddr用于函数参数

NBO网络字节顺序 和 HBO本机字节顺序 的转换
inet_addr()    将字符串转换为NBO网络地址
inet_ntoa ()   将NBO地址转化成字符串点数格式
htons()        将short类型转换为网络类型的端口号
ntohs()        将NBO类型的端口转换为short类型的变量

   1: //代码例子
   2: struct sockaddr_in cliaddr;
   3: bzero(&cliaddr,sizeof(cliaddr)); //网络地址的初始化
   4: ina.sin_family=AF_INET;  //INET通信类型
   5: ina.sin_port = htons(23);  //指定端口,将host的变量转换为NBO的端口号
   6: ina.sin_addr.s_addr = inet_addr("132.241.5.10"); //将字符串转换为NBO地址
   7:  
   8: char* szIp = inet_ntoa(ina.sin_addr);//inet_ntoa  将NBO地址转化成字符串点数格式
   9: short port = ntohs(ina.sin_port);//ntohs 将NBO端口转换为short变量

5.socket的创建和设置

socket创建和设置,及其设置(tcp、udp流程类似)

   1: //建立一个侦听socket
   2: int create_listener(int port)
   3: {
   4:     int fd;
   5:  
   6:     //创建一个inet类型的socket
   7:     if ((fd = socket(AF_INET, SOCK_STREAM, 0)) <= 0)
   8:         return -1;
   9:  
  10:  
  11:     int opt=1;
  12:     
  13:     /* SO_REUSEADDR,只定义一个套接字在一个端口上进行监听,
  14:      * 如果服务器出现意外而导致没有将这个端口释放,
  15:      * 那么服务器重新启动后,你还可以用这个端口,因为你已经规定可以重用了,
  16:      如果你没定义的话,你就会得到提示,ADDR已在使用中 
  17:      */
  18:     setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void*)&opt, sizeof(opt));
  19:     set_nonblock(fd);
  20:  
  21:  
  22:     /* sockaddr结构体的缺陷:sa_data把目标地址和端口信息混在一起了
  23:      * sockaddr_in 结构体: 把port和addr 分开储存在两个变量中
  24:      * sockaddr 和 sockaddr_in的相互关系
  25:      */
  26:     struct sockaddr_in sin;
  27:     memset(&sin, 0, sizeof(struct sockaddr_in));
  28:     sin.sin_family = AF_INET;
  29:     sin.sin_port = htons(port);       // 指定port端口
  30:     sin.sin_addr.s_addr = INADDR_ANY; // ip地址不做绑定使用机器默认的ip地址
  31:     if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) != 0)
  32:     {
  33:         close(fd);
  34:         return -1;
  35:     }
  36:  
  37:     
  38:  
  39:     // 开始侦听该端口
  40:     if (listen(fd, 32) != 0)
  41:     {
  42:         close(fd);
  43:         return -1;
  44:     }
  45:  
  46:     return fd;
  47: }

6. socket的接收数据

socket接收数据要注意:
1. 是否是阻塞模式;如果是阻塞模式,没有读到数据会持续等待;非阻塞则返回;如果要根据错误代码和接收模式及其收到数据的个数,来判断是客户端已经断开?网络错误?
2. udp如果有数据则就是一整包数据,而tcp是流会出现粘包和半包数据的情况

   1: //socket数据接收,可以Window可以linux
   2: while(rs)
   3: {
   4:     //接收数据,默认接收sizeof(buf)大小,实际接收buflen大小
   5:     buflen = recv(sock_fd, buf, sizeof(buf), 0);
   6:     if(buflen < 0)
   7:     {
   8:         // 由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可读;在这里表示该事件已做处理
   9:         if(errno == EAGAIN)
  10:             break;
  11:         else
  12:             return;//表示有错误发生
  13:     }
  14:     else if(buflen == 0)
  15:     {
  16:         // 这里表示对端的socket已正常关闭.
  17:     }
  18:  
  19:  
  20:     if(buflen == sizeof(buf)
  21:         rs = 1;   // 需要再次读取
  22:     else
  23:         rs = 0;
  24: }

7. socket的发送数据

◇.send函数最后一个参数
window: 一般是0
linux: 最好设置为MSG_NOSIGNAL;表示出错后不向系统发信号,否则程序会退出!

   1: /* socket发送数据,可以在Window下,可以在linux下
   2:  * 
   3:  * 该代码是采用阻塞模式下,完整的发送一个缓冲中的数据
   4:  * 如果缓冲区满,则阻塞线程等待缓冲区有空间再发送
   5:  * 如果网络错误,或socket错误,会立即返回-1
   6:  */
   7: int socket_send(int sockfd, const char* buffer, size_t buflen)
   8: {
   9:     int tmp;
  10:     size_t total = buflen;
  11:     const char *p = buffer;
  12:  
  13:     while(1)
  14:     {
  15:         /* window: 一般是0
  16:          * linux: 最好设置为MSG_NOSIGNAL;表示出错后不向系统发信号,否则程序会退出!
  17:          */
  18:         tmp = send(sockfd, p, total, 0);
  19:         if(tmp < 0)
  20:         {
  21:             //当进程收到信号会中断正在进行的系统调用、区处理信号,处理完系统返回-1且errno==Eintr;
  22:             //所以可continue继续执行
  23:             if(errno == EINTR)
  24:                 continue;
  25:  
  26:  
  27:             // Eagain表示写缓冲队列已满, usleep后继续发送
  28:             if(errno == EAGAIN)
  29:             {
  30:                 usleep(1000);
  31:                 continue;
  32:             }
  33:  
  34:  
  35:             return -1;
  36:         }
  37:  
  38:  
  39:         if((size_t)tmp == total)
  40:             return buflen;
  41:  
  42:  
  43:         total -= tmp;
  44:         p += tmp;
  45:     }
  46:  
  47:  
  48:     return tmp;
  49: }

8. socket的select模式

9. socket的epoll模式

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, epoll_event *event);
        op:Add,Mod,Del
        event {events,epoll_da ta_t da ta}
        events:epollIN/out/pri/err/hup/et
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

ET:高速模式,支持无阻塞;当fd从未就绪变成就绪epoll将通知你,然后它会假设你已经知道了该通知且不会再次发出通知、知道你做了操作将fd变为未就绪状态;所以如果你一直对某个fd没有操作,那么内核将一直不发出该fd的通知!
LT:同ET相反,它会一直发出fd的通知!
ET模式:在epollIN事件时,recv返回的大小等于请求大小,那么很有可能缓冲区还有数据未读完,也意味着该次事件没有处理完,还需要再次读取; 参考数据完整接收
ET模式:由于epoll是无阻塞那么send虽然返回但是数据并没有真正发出去,当缓冲区满后会产生Eagain错误,同时不会理此次待发送的数据,因此封装一个socket_send函数来处理该问题,如果返回eagain就阻塞在该函数中[EAGAIN 另外一个名字是 EWOULDBLOCK]  参考数据的完整发送

   1: /* 这是一个简单的epoll模式的程序,
   2:  *
   3:  */
   4: int main()
   5: {
   6:     //1. 创建epoll描述符, 可接受5000个连接
   7:     epoll_fd = epoll_create(5000);
   8:  
   9:     //2. 创建、绑定和侦听socket; 并且将它放到epoll上面开始侦听数据到达和connect连接
  10:     listen_fd = create_listener(port);
  11:   epoll_event ev;
  12:     ev.data.fd = cfd;
  13:     ev.events = EPOLLIN | EPOLLET;
  14:     epoll_ctl(epoll_fd, EPOLL_CTL_ADD, cfd, &ev);//检查cfd上面的数据到达
  15:  
  16:  
  17:     //3.创建线程、并脱离创建者;在线程中检索epoll上面的触发的socket
  18:     pthread_attr_init(&attr);
  19:     pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
  20:     pthread_create(&tid, &attr, epoll_function, NULL);
  21: }
  22:  
  23:  
  24: // 专门进行侦听epoll的线程函数
  25: void *epoll_function(void *p) 
  26: {
  27:     while ( 1)
  28:     {
  29:         // 等待epoll上面的事件触发,然后将他们的fd检索出来
  30:         struct epoll_event events[EPOLL_EVENT_MAX];
  31:         int evn_cnt = epoll_wait(epfd, events, EVENTSIZE , -1);
  32:  
  33:         // 循环处理检索出来的events队列中的每个fd
  34:         for (int i=0; i<wait_cnt; i++)
  35:         {
  36:             if (events[i].data.fd == listen_fd)   // 有客户端connect过来
  37:             {
  38:                 struct sockaddr_in caddr;
  39:                 socklen_t clen = sizeof(caddr);
  40:  
  41:                 // 接收连接
  42:                 int connfd = accept(listen_, (sockaddr *)&caddr, &clen);
  43:                 if (connfd < 0)
  44:                     continue;
  45:  
  46:                 // 将新接收的连接的socket放到epoll上面
  47:                 struct epoll_event ev;
  48:                 ev.data.fd = connfd;
  49:                 ev.events = EPOLLIN|EPOLLET;
  50:                 epoll_ctl(epoll_, EPOLL_CTL_ADD, connfd, &ev);
  51:             }
  52:             else if (events[i].events & EPOLLIN)       // 有数据到达
  53:             {
  54:                 //接收数据
  55:                 recv(event[i].data.fd, buffer, sizeof(buffer), 0);
  56:             }
  57:         }
  58:     }
  59: }

10.Window和linux的socket的不同之处:

◇ .头文件
Window:winsock.h或winsock2.h
linux: netinet/in.h(大部分都在这),unistd.h(close函数)
◇ .初始化
window: 需要WSAStartup启动Ws2_32.lib;#program comment(lib, "Ws2_32")加载lib库
linux: 无需
◇ .关闭socket
window: closesocket()
linux: close()
◇ .socket的类型
window: SOCKET
linux: int
◇ .错误代码的获取
window: getlasterror()/WSAGetLastError()
linux: 为成功返回-1;错误代码在errno中(头文件errno.h)
◇ .设置非阻塞
window: ioctlsocket()
linux: fcntl(), (头文件fcnt.h)
◇ .send函数最后一个参数
window: 一般是0
linux: 最好设置为MSG_NOSIGNAL;表示出错后不向系统发信号,否则程序会退出!
◇. 多线程
window:包含process.h,用_beginthread/_endthread
linux:包含pthread.h,用pthread_create/pthread_exit
◇.使用IP地址
window:addr_var.sin_addr.S_un.S_addr
linux:addr_var.sin_addr.s_addr

11.创建线程

我们先看一个简单的创建线程的代码
   1: // 创建脱离创建者的线程
   2: pthread_t tid;
   3: pthread_attr_t attr;
   4: pthread_attr_init(&attr);
   5:  
   6: // 创建线程PTHREAD_CREATE_DETACHED(分离线程),PTHREAD _CREATE_JOINABLE(非分离线程)
   7: // 分离线程是不能join的,非分离线程可以join
   8: // 一般来说服务器大多采用分离线程
   9: pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  10:  
  11: //创建线程;指定线程属性、指定线程函数
  12: if (pthread_create(&tid, &attr, serv_epoll, NULL) != 0)
  13: {
  14:     fprintf(stderr, "pthread_create failed\n");
  15:     return -1;
  16: }

//下面是一个小的线程
http://blog.chinaunix.net/u1/33885/showart_265060.html

http://blog.163.com/miky_sun/blog/static/3369405200992593735641/

Linux下socket(select,epoll)相关推荐

  1. Linux下Socket编程

    Linux下Socket编程    网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符.Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的S ...

  2. C++网络编程快速入门(二):Linux下使用select演示简单服务端程序

    目录 select参数解释 select使用规范 select使用缺点 基本流程 实例代码 通信效果演示 往期文章 select参数解释 extern int select (int __nfds, ...

  3. LINUX下Socket编程 函数格式详解

    你需要了解的一些系统调用: socket() bind() connect() listen() accept() send() recv() sendto() recvfrom() close() ...

  4. Linux之poll/select/epoll代码示例

    Linux poll and epoll poll 问题:假如应用需要根据IO的状态来读或写多个IO,如何处理?如果是一个进程处理,一个一个IO的处理,那么就势必会出现阻塞等待某个IO的过程,此时就可 ...

  5. linux下socket和MySQL数据库编程

    一.基本socket函数 Linux系统是通过提供套接字(socket)来进行网络编程的.网络的socket数据传输是一种特殊的I/O,socket也是一种文件描述符.socket也有一个类似于打 开 ...

  6. Linux下Socket相关头文件总结

    一 三种类型的套接字: 1.流式套接字(SOCKET_STREAM)     提供面向连接的可靠的数据传输服务.数据被看作是字节流,无长度限制.例如FTP协议就采用这种. 2.数据报式套接字(SOCK ...

  7. Linux下socket编程之UDP简单实现

    本文实现一个简单的UDP小例子,来说明Linux下socket编程之UDP的简单实现.本文主要包括三个部分:服务器端的实现,客服端的实现和通信测试.实现的功能:客服端发送一条消息给服务器端,服务器端把 ...

  8. 一文了解linux下socket编程

    一文了解linux下socket编程 文章目录 一文了解linux下socket编程 1 网络编程的相关简述 1.1 引言 1.2 Tcp和Udp简介 1.3 TCP三次握手和四次挥手 1.4 网络编 ...

  9. Linux下Socket编程之TCP应用

    现在,我们用前面所构建的socket类,重新设计<Linux下Socket编程之TCP Server端>中echo的服务器,然后设计客户端程序. echo服务器的工作原理很简单: 1.接收 ...

最新文章

  1. flash绘图API:恋上你的CD
  2. 【c语言】输入输出格式练习
  3. python 文件的操作
  4. svd协同过滤java实现_利用 SVD 实现协同过滤推荐算法
  5. Asp.Net中几种相似数据绑定标记符号的解释及用法
  6. PHP生成Mysql数据字典
  7. html5 head 标签
  8. Flask项目--注册
  9. 机器学习之线性回归(matlab)
  10. C++访问控制符内容相关介绍
  11. vue ---- 将项目打包发布
  12. 小学计算机课教后小记,小学信息技术课开展生活化教学的方法
  13. java高校贫困生助学贷款系统ssm框架
  14. 为什么要学习现代控制理论(机器人方向)?
  15. 离线语音控制系统 唤醒词.命令字
  16. 航天飞机电子计算机象征是什么,航天飞机的特点是什么
  17. 走出NASA,向大地“下战书”,他要用卫星遥感数据改变中国农业
  18. 系统架构中概念总结二
  19. Win10(家庭版)修改中文用户名为英文
  20. IDM下载器的安装与使用

热门文章

  1. 平面设计配色全攻略,不看你将错过1个亿!
  2. MT6169 Layout设计资料参考
  3. 计算机专业可以进水利高级职称,怎样评高级水利工程师?需要满足哪些条件?
  4. matlab candle横坐标日期显示不对
  5. 短距离无线电测向的基本方法和基本技术,可归纳为下列几个方面:
  6. 服务器V6型号什么意思,ipv6接口是什么意思
  7. Handbrake编译、调试
  8. elasticsearch 浏览器访问不到
  9. Spark Standalone和yarn区别
  10. 远程服务器注销会话,当远程桌面会话注销时,如何停止终止应用程序服务器的DLL?...