这两天在学Linux下的网络编程,于是便看了些关于socket和epoll的资料。
  首先介绍socket,socket编程我之前也接触过,不过是在windows下接触的。和windows不同的是,windows下关于socket编程,是直接给你一个socket的类,在上面建立自己的实例。而在linux中,你在建立socket时,它会给你一个文件描述符(其实就是一个整数),这个整数和内核为你建立的socket相联系,这个整数其实就代表着建立的socket(在网上查到的是说,linux下一切皆文件,socket其实也就是一种特殊的文件,而文件用文件描述符来标记)。接着就是将这个文件描述符(以下以sockfd代替)用bind函数与地址绑定(之后详细解释),如果是监听socket就开始listen,如果是连接socket就与server连接。其实感觉无论在windows上还是在linux上,socket都是这么使用的,下面讲解下在这个过程中使用的函数。
  首先是使用socket函数,原型如下:

int socket(int domain, int type, int protocol);

  函数返回一个整型值,就是所建立的socket的文件描述符。当返回值为-1是,说明建立socket失败。第一个参数domain指定,它用于确定所建立的socket的通信域,例如AF_INET就是ipv4,第二个参数type,指定建立的socket的类型,它定义了通信的语义,例如SOCK_STREAM提供顺序,可靠,双向,基于连接的字节流。最后一个参数protocol指定与套接字相匹配的协议,通常,只有单个协议存在以支持给定协议族内的特定套接字类型,在这种情况下协议可以被指定为0。但是可能存在多个协议和套接字匹配,此时就要手动指定协议。(三个参数的具体取值查询man)。
在建立了socket后,就是使用bind函数将其与地址绑定在一起。

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  当绑定成功后,函数返回0,失败时返回-1,此时可查询errno来确定错误原因。此函数有三个参数,第一个参数就是你需要绑定的socket的文件描述符。第二个参数是需要绑定的地址,第三个参数是地址的长度(字节数)。对于第二个参数,对于ip协议,常使用sockaddr_in来代替,在绑定的时候强制转换成sockaddr,((struct sockaddr*)&addr,addr为一个sockaddr_in类型的参数)。

struct sockaddr_in
{sa_family_t    sin_family; /* address family: AF_INET */in_port_t      sin_port;   /* port in network byte order */struct in_addr sin_addr;   /* internet address */
};/* Internet address. */
struct in_addr
{uint32_t       s_addr;     /* address in network byte order */
};

  对于ip协议,sin_family永远被设为AF_INET,sin_port为端口号,sin_addr为具体的IP地址,可以将其设为INADDR_ANY来指定为任意ip地址(也可以近似于认为是本机地址),使用“127.0.0.1”来指定本机地址,或者自定义ip地址。使用inet_aton来将字符串形式的ip地址转换为标准的IP地址形式,int inet_aton(const char *cp, struct in_addr *inp),第一个参数是字符串形式ip地址,第二个参数是需要得到的地址,还有一些其他转换的方式,具体见man。最后,注意主机字节序和网络字节序的差别。
绑定成功后,便可以开始监听socket了,使用listen函数:

int listen(int sockfd, int backlog);

  和bind函数一样,若是函数成功,返回0,若是失败,返回-1。第一个参数是监听socket的文件描述符,第二个参数指定sockfd监听队列的最大长度,当sockfd的监听队列已满时,若还有新的连接,便会出现错误。
  对于客户端的连接socket,使用connect函数连接到服务器socket上。(貌似对于客户端socket不需要绑定地址,系统会自动为其指派地址和端口,这个具体还不太清楚,之后若是确定了会写出来)。

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  函数的返回值规则和上述函数一样,0为成功,-1为失败。第一个参数为client的socket的文件描述符,后面两个参数则分别为所要连接到的server的ip地址和地址长度。地址使用和bind函数一样。
  这样,便完成了client和server的连接。
  连接成功后便可以使用send和recv函数来收发数据了。具体的例子如下(先只放出client的例子,server的代码之后和epoll代码一起放出。):
  client:

#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<iostream>
#include<netinet/in.h>
#include<unistd.h>
#include<errno.h>
#include<arpa/inet.h>using namespace std;int main(void)
{int ClientFd;sockaddr_in ClientAddr;ClientFd=socket(AF_INET,SOCK_STREAM,0);if(ClientFd==-1){cout<<"Client socket created falied!!"<<errno<<endl;return 0;}int ServerFd;sockaddr_in ServerAddr;ServerAddr.sin_family=AF_INET;if(inet_aton("127.0.0.1",&ServerAddr.sin_addr)==0){cout<<"server IPAddress error!!"<<endl;return 0;}string ipAddress=inet_ntoa(ServerAddr.sin_addr);cout<<ipAddress<<endl;ServerAddr.sin_port=htons(8000);socklen_t ServerLen=sizeof(ServerAddr);if(connect(ClientFd,(struct sockaddr*)&ServerAddr,ServerLen)==-1){cout<<"can't connect to server!!"<<endl;cout<<errno<<endl;return 0;}const char *buffer="Hello, My Server!!";send(ClientFd,buffer,18,0);shutdown(ClientFd,SHUT_RDWR);if(close(ClientFd)==-1)cout<<"close Client failed"<<endl;return 0;
}

-------------------------------------------------------------------华丽的分界线---------------------------------------------------------------------------

 以上就是关于我关于socket的一些理解。下面介绍下epoll。在学习关于epoll之前,我曾经使用过完成端口,感觉和完成端口相比,epoll的使用就简单很多了,只需要三个函数,epoll_create,epoll_vtl,和epoll_wait.当然完成epoll的第一步就是先建立一个监听socket,将其作为第一个socket加进epoll的文件描述符中,之后每有一个客户端连接到这个sockfd时,就将这个客户端加进epoll中。首先介绍epoll_create函数:
int epoll_create(int size);

  在过去的版本中,size参数用来指定能在epoll中添加的sockfd的数量,但从Linux 2.6.8开始,size参数被忽略,但必须为一个大于0的数。此函数返回一个新的epoll的实例的文件描述符,此文件描述符用于之后对epoll的操作。当不再需要此文教描述符时,应该使用close函数关闭此文件描述符,当所有引用此epoll实例的文件描述符关闭时,系统内核会销毁此epoll实例以释放资源(这句话应该也说明了能够以不同的文件描述符调用一个epoll实例)。当函数返回-1时,说明函数失败,可以查看errno来确定错误原因。
v在创建了epoll实例,得到引用其的文件描述符之后,就可以调用epoll_ctl函数将已经建立好的监听socket加入epoll队列中了。

 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

  epoll_ctl有四个参数,第一个参数epfd为epoll的文件描述符,也就是刚刚使用epoll_create建立的epoll文件描述符。第二个参数op(operation),它指定需要对目标文件描述符fd进行的动作,op参数有效值为以下三个:EPOLL_CTL_ADD,它将目标文件描述符fd注册到epoll的队列之中,并且使fd文件描述符所引用的文件和event(第四个参数)相关联;EPOLL_CTL_MOD,用来改变目标文件描述符fd相关联的事件event;EPOLL_CTL_DEL,用来将目标文件描述符fd,从epoll队列中移除,在这个操作下,event参数被忽略,可以被设置为NULL。第三个参数fd就是需要被操作的目标文件描述符fd。第四个参数event描述和fd连接到一起的操作(请原谅我的语文水平,不过看到对参数值的讲解时应该都能够理解event的含义)。epoll_event的结构如下:

        typedef union epoll_data {void        *ptr;int          fd;uint32_t     u32;uint64_t     u64;} epoll_data_t;struct epoll_event {uint32_t     events;      /* Epoll events */epoll_data_t data;        /* User data variable */};

  其中events变量是一组位掩码,可以使用|来同时选中几个不同的events参数。可选择的参数如下:EPOLLIN,相关文件可用于read操作;EPOLLOUT,相关文件可用于write操作;对于stream socket来说,可以检测出对面客户端关闭(close)或者半关闭连接(shutdown)(但我在代码中测试过,没有能够成功检测出来,可能是我的代码写错了,但是可以用recv来检测对面是否关闭连接,如果recv的返回值为0的话说明对面关闭了连接,这点我测试过);还有一些其他的events参数,如EPOLLPRI,EPOLLERR等,我也没有进行测试,可以查询man来了解具体含义。
  在将监听socket注册到epoll中后,便可以调用epoll_wait来开始进行epoll的监听工作。

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

  epoll_wait函数等待发生在epoll队列中的文件描述符的事件,如果没有事件发生,它会阻塞住程序。第一个参数epfd还是epoll实例的文件描述符。第二个参数events指向记录发生的事件的内存区域,可用events[i]来调用发生的事件。第三个参数maxevents指定epoll所能返回的最大的事件数量,此参数必须大于0,第四个参数timeout指定epoll_wait阻塞住程序的超时时间(epoll_wait将会阻塞住程序直到以下三种情况之一发生:epoll队列中的一个文件描述符上发生了事件,epoll-wait被信号中断,超时时间到)。当timeout为-1时会导致如果没有事件到达,程序将会被无限期阻塞住,而如果timeout为0,epoll-wait将会立即返回,即使任何事件都没有发生。epoll-wait函数返回发生的事件数目,如果为0,说明超时,没有时间发生,如果返回-1,说明函数错误,可查询errno来确定错误原因。
  在调用完epoll_wait后,如果有客户端连接到监听socket,便可以接收到,接收到后便新建一个sockfd来专门和这个客户端进行通信,再调用epoll_ctl函数将这个sockfd注册到epoll中,如此循环便可。程序如下:
epoll.h:

#include<sys/socket.h>
#include<sys/epoll.h>
#include<sys/types.h>
#include<iostream>
#include<string>
#include<errno.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<unistd.h>#define MAX_SIZE 500
#define BUFF_SIZE 1000
#define MAX_EVENTS 100using namespace std;
class myEpollServer
{public:myEpollServer(){};~myEpollServer(){};int setnonblocking(int socketFd);void start();bool initializeEpoll(int maxSize);bool initializeServerSocket();
private://listen socket infoint ServerFd=0;sockaddr_in ServerAddr;int ServerPort;
};

  epoll.cpp

#include"MyEpollPort.h"bool myEpollServer::initializeServerSocket()
{using namespace std;if((ServerFd=socket(AF_INET,SOCK_STREAM,0))==-1){cout<<"create server socket fail!!,error:"<<strerror(errno)<<endl;return false;}   memset(&ServerAddr,0,sizeof(ServerAddr));ServerAddr.sin_family=AF_INET;ServerAddr.sin_addr.s_addr=htons(INADDR_ANY);ServerAddr.sin_port=htons(8000);if(bind(ServerFd,(struct sockaddr*)&ServerAddr,sizeof(ServerAddr))==-1){cout<<"bind server addr fail,error:"<<strerror(errno)<<endl;return false;}   if(listen(ServerFd,10)==-1){cout<<"server listen fail,error:"<<strerror(errno)<<endl;return false;}  return true;
}bool myEpollServer::initializeEpoll(int maxSize)
{struct epoll_event ev,events[100];int connectFd,nfds,epollFd;epollFd=epoll_create(maxSize);if(epollFd==-1){perror("epoll_create");return false;}ev.events=EPOLLIN|EPOLLRDHUP;ev.data.fd=ServerFd;if(epoll_ctl(epollFd,EPOLL_CTL_ADD,ServerFd,&ev)==-1){perror("epoll_ctl:ServerFd");return false;}for(;;){nfds=epoll_wait(epollFd,events,MAX_EVENTS,-1);if(nfds==-1){perror("epoll_wait");close(ServerFd);return false;}for(int i=0;i<nfds;++i){cout<<i<<endl;if(events[i].data.fd==ServerFd){cout<<1<<endl;sockaddr_in clientAddr;socklen_t len;connectFd=accept(ServerFd,(struct sockaddr*)&clientAddr,&len);if(connectFd==-1){perror("accept");close(ServerFd);return false;}cout<<"client addr is: "<<inet_ntoa(clientAddr.sin_addr)<<endl;setnonblocking(connectFd);ev.events=EPOLLIN|EPOLLET;ev.data.fd=connectFd;if(epoll_ctl(epollFd,EPOLL_CTL_ADD,connectFd,&ev)==-1){perror("epoll_ctl:connectFd");close(ServerFd);return false;}}else{cout<<2<<endl;if(events[i].events&EPOLLRDHUP||events[i].events&EPOLLERR){sockaddr_in addr;socklen_t len=sizeof(addr);if(getpeername(events[i].data.fd,(struct sockaddr*)&addr,&len)==-1){cout<<"get client address fail!1"<<endl;}cout<<"client:"<<inet_ntoa(addr.sin_addr)<<"out of link!!"<<endl;}char buff[BUFF_SIZE]; int n=recv(events[i].data.fd,buff,1000,0);if(n==0){sockaddr_in addr;socklen_t len=sizeof(addr);if(getpeername(events[i].data.fd,(struct sockaddr*)&addr,&len)==-1){cout<<"get client address fail!1"<<endl;}cout<<"client:"<<inet_ntoa(addr.sin_addr)<<"out of link!!"<<endl;}buff[n]='\0';cout<<buff<<endl;}}}return true;}void myEpollServer::start()
{initializeServerSocket();initializeEpoll(100);
}int myEpollServer::setnonblocking(int sockFd)
{if(fcntl(sockFd,F_SETFL,fcntl(sockFd,F_GETFD,0)|O_NONBLOCK)==-1){return -1;}return 0;
}

linux下socket编程和epoll的使用相关推荐

  1. Linux下Socket编程

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

  2. 一文了解linux下socket编程

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

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

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

  4. linux下socket编程读写函数

    linux下socket编程,实现服务器与客户端的通信之后,在同一个虚拟机上,打开两个shell,一个运行服务器程序,一个运行客户端程序,课相互发送数据. 如果使用的是recv接收函数,当关闭客户端或 ...

  5. [zz]Linux 下 socket 编程示例

    本示例为 Client/Server 结构,通过代码演示 Client 如何建立连接,并向远程端发送数据:Server 端如何侦听系统连接请求,接收请求并建立连接,进而获取客户端发来的数据.代码虽短, ...

  6. linux下socket编程中setsockopt的作用

    如题所示,在linux进行socket编程的时候,一般而言,socket,bind,listen三步曲之后,就开始接收客户端请求,然后实现收发数据. 如下所示的代码,是没有setsockopt的情况: ...

  7. linux socket 结构定义 send,Linux下Socket编程中用send发送结构体

    Linux网络通信 Linux下多客户端聊天软件 最近在开发一个Linux下的聊天软件,好久没有做C语言的开发了,感觉到很多东西已经生疏了,这下又碰到用Socket传递结构体的问题,google了一下 ...

  8. C++服务器(一):了解Linux下socket编程

    最近想要用C++写个socket的服务器,用于日常的项目开发. 不过,我是新手,那就慢慢地学习一下吧. 首先,先写一段程序,用起来先. 感谢博文: Linux下 C++调用C 实现socket网络通讯 ...

  9. 【Linux】Linux 下socket 编程

    TCP/IP协议叫做传输控制/网际协议,又叫网络通信协议 TCP/IP虽然叫传输控制协议(TCP)和网际协议(IP),但是实际上是一组协议,包含ICMP, RIP, TELENET, FTP, SMT ...

最新文章

  1. c语言从stdin读入
  2. 在React Hook里使用history.push跳转
  3. mysql配置_Mysql配置 max_allowed_packet
  4. android wear无法启用,android-wear – 无法创建Android虚拟设备,“没有为此目标安装系统映像”...
  5. 关于开发WPF的一些感想
  6. python有什么用途和价值-Python是什么 Python的用处
  7. mysql中timestamp类型数据查找中出现的问题
  8. 刚刚字节跳动发布了1200个java岗位,平均薪资40k
  9. EBS FORM开发问题总结
  10. 用CSS让文字居于div的底部
  11. 浮动特性-脱标(HTML、CSS)
  12. ZZULIOJ 1917: E
  13. amCharts: JavaScript/HTML5 charts 破解
  14. 创建CrossApp工程
  15. iOS动画和特效(二)UIKit力学行为
  16. php实现室内地图导航,叠加室内地图-室内地图-示例中心-JS API 示例 | 高德地图API...
  17. 网页字体生成器「谷雨解字」——不仅仅是中文字体子集化工具
  18. 9、Linux文本处理三剑客之sed命令
  19. Error while extracting response for type [class xxx] and content type application/xml;charset=UTF-8
  20. ubuntu下安装(二)印象笔记(中国版而不是国际版)

热门文章

  1. Android 前置摄像头的默认是180度,导致应用拍照和录制视频是倒立的问题修改
  2. ADO对象之Command总结
  3. 微服务版单点登陆系统(SSO)
  4. linux ldd 移植
  5. 火线精英显示服务器一断,火线精英黑屏的处理办法总结
  6. Vive开发之VR射箭
  7. 【TensorFlow】freeze_graph
  8. Jetson TX1配置与踩坑历程
  9. android layoutinflater 高度,探究LayoutInflater和RecyclerView中item设置宽高无效
  10. 如何制作照片蒙太奇效果?