Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1184975
  • 博文数量: 573
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 66
  • 用 户 组: 普通用户
  • 注册时间: 2016-06-28 16:21
文章分类

全部博文(573)

文章存档

2018年(3)

2016年(48)

2015年(522)

分类: LINUX

2015-12-02 15:33:26


点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <sys/types.h>
  4. #include <sys/socket.h>
  5. #include <netinet/in.h>
  6. #include <arpa/inet.h>
  7. #include <sys/wait.h>
  8. #include <pthread.h>
  9. #include <stdlib.h>
  10. #include <time.h>
  11. #include <unistd.h>
  12. #include <sys/stat.h>
  13. #include <fcntl.h>
  14. #include <errno.h>
  15. #include <termios.h>
  16. #include <sys/time.h>
  17. #include <sys/timeb.h>
  18. #include <stdarg.h>
  19. #include <netdb.h>
  20. #include <poll.h>
  21. #include <sys/epoll.h>

  22. #define SER_IP "127.0.0.1" /*服务器的IP地址*/
  23. #define OPEN_MAX 512

  24. int read_socket(int socket_id, char *pbuf, int len);
  25. int write_socket(int socket_id, char *pbuf, int len);

  26. int main(int argc, char ** argv) /*服务端程序*/
  27. {    
  28.     char    serv_ip[20];
  29.     char    guest_ip[20];
  30.     memset(serv_ip, 0, sizeof(serv_ip));
  31.     memset(guest_ip, 0, sizeof(guest_ip));
  32.     
  33.     int count;
  34.     pid_t pid;
  35.     char ip_buf[32];
  36.     char readbuf[1024];
  37.     int sockfd = -1; /*监听套接字*/
  38.     int newfd = -1; /*连接套接字*/
  39.     int ret = -1;
  40.     int opt = 1;
  41.     int len;
  42.     int i=0;
  43.     int j=0;
  44.     int read_count = 1; /*读取次数*/
  45.     struct sockaddr_in seraddr; /*用来保存服务器自己的ip地址和端口号的*/
  46.     struct sockaddr_in cliaddr; /*用来保存连接过来的客户的ip地址和端口号的*/
  47.     struct sockaddr_in bindaddr; /*用来保存连接过来的客户的ip地址和端口号的*/
  48.     struct sockaddr lbindaddr;
  49.     
  50.     memset(ip_buf, 0, sizeof(ip_buf));
  51.     memset(readbuf, 0, sizeof(readbuf));
  52.     
  53.     memset(&seraddr, 0, sizeof(seraddr));
  54.     memset(&cliaddr, 0, sizeof(cliaddr));
  55.     memset(&bindaddr, 0, sizeof(bindaddr));
  56.     memset(&lbindaddr, 0, sizeof(lbindaddr));
  57.     
  58.     sockfd = socket(AF_INET, SOCK_STREAM, 0); /*创建连接套接字,TCP协议*/
  59.     if(sockfd < 0)
  60.     {
  61.         return -1;
  62.     }
  63.     setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); /*将服务端设置具有端口号可以重复*/

  64.     seraddr.sin_family = AF_INET; /*指定服务器地址协议族*/
  65.     seraddr.sin_port = htons(4989);     /*指定要监听的服务器的端口号*/
  66.     /*seraddr.sin_addr.s_addr = INADDR_ANY;*/ /*指定要监听的服务器的ip:0.0.0.0:则客户端可连 127.0.0.1 和 真实的主机地址*/
  67.     
  68.     char * hostname = NULL;
  69.     hostname = getenv("HOST");
  70.     if(hostname == NULL)
  71.     {
  72.         printf("HOST环境变量不存在!\n");
  73.         exit(-1);
  74.     }
  75.     struct hostent * myhost;
  76.     myhost = gethostbyname(hostname);
  77.     if(myhost == NULL)
  78.     {
  79.         printf("myhost is null!\n");
  80.         exit(-1);
  81.     }
  82.     bcopy(myhost->h_addr, &seraddr.sin_addr, myhost->h_length);
  83.     char ipbuf[128];
  84.     memset(ipbuf, 0, sizeof(ipbuf));
  85.     inet_ntop(AF_INET, &seraddr.sin_addr.s_addr, ipbuf, sizeof(ipbuf));
  86.     printf("主机名=[%s],IP地址=[%s]\n", myhost->h_name, ipbuf);
  87.     printf("IP地址=[%s]\n", ipbuf);

  88.     ret = bind(sockfd, (struct sockaddr*)&seraddr, sizeof(struct sockaddr)); /*将IP地址,端口号和文件绑定,为套接字命名*/
  89.     if(ret < 0)
  90.     {
  91.         printf("bind err! ret = [%d]\n", ret);
  92.         return -1;
  93.     }
  94.     
  95.     ret = listen(sockfd, 10); /*监听*/
  96.     if(ret < 0)
  97.     {
  98.         return -1;
  99.     }
  100.     printf("监听sockfd=[%d]\n", sockfd);
  101.     sleep(10);
  102.     
  103.     /*epoll_create(int size);
  104.     功能:创建一个epoll句柄,
  105.     参数:size :用来告诉内核,这个监听的数目一共有多大,但是又不同于select的第1个参数
  106.     注意:当创建好epoll句柄后,它就是会占用一个fd值,
  107.                 在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,
  108.                 所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。*/
  109.     int epfd;
  110.     epfd = epoll_create(OPEN_MAX);
  111.     
  112.     /*int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
  113.     功能:事件注册函数,注册要监听的事件的类型
  114.     参数:epfd :epoll_create()的返回值
  115.                 op :表示动作,有3个宏:
  116.                 EPOLL_CTL_ADD:注册新的fd到epfd中;
  117.                 EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
  118.                 EPOLL_CTL_DEL:从epfd中删除一个fd;
  119.                 fd :需要监听的fd
  120.                 event : 告诉内核需要监听什么事,
  121.                 struct epoll_event结构如下:
  122.                 struct epoll_event
  123.                 {
  124.                         __uint32_t events; // Epoll events
  125.                         epoll_data_t data; // User data variable
  126.                 };
  127.                 typedef union epoll_data
  128.                 {
  129.                         void *ptr;
  130.                         int fd;
  131.                         __uint32_t u32;
  132.                         __uint64_t u64;
  133.                 }epoll_data_t;//保存触发事件的某个文件描述符相关的数据
  134.                 
  135.                 events可以是以下几个宏的集合:
  136.                 EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
  137.                 EPOLLOUT:表示对应的文件描述符可以写;
  138.                 EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
  139.                 EPOLLERR:表示对应的文件描述符发生错误;
  140.                 EPOLLHUP:表示对应的文件描述符被挂断;
  141.                 EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
  142.                 EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。
  143.     */
  144.     struct epoll_event ev;
  145.     ev.data.fd = sockfd;
  146.     ev.events = EPOLLIN;
  147.     epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev); //将套接字sockfd的可读事件,注册到epfd中去。epfd中可以注册多个fd的事件
  148.     
  149.     struct epoll_event evs[OPEN_MAX];
  150.     
  151.     int num;
  152.     while(1)
  153.     {
  154.             /*int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)
  155.             功能 :等待事件的产生,类似于select()调用。
  156.             参数 :events :输出参数,返回从内核得到的事件的集合=包括fd+事件的类型,
  157.                          maxevents :表示每次能处理的最大事件数,告之内核这个events有多大,
  158.                                         这个maxevents的值不能大于创建epoll_create()时的size,
  159.                          timeout : 是超时时间(毫秒),0会立即返回,-1将不确定,也有说法说是永久阻塞。
  160.             函数返回值 :需要处理的事件数目,如返回0:表示已超时,并且在timeout时间内,没有监听的事件发生。
  161.             */
  162.             num = epoll_wait(epfd, evs, 512, 5000); //查询epfd中注册的事件是否已经发生
  163.             printf("num=[%d]\n", num);
  164.             for (i=0; i<num; i++) /*因为只注册了fd的可读事件,所以num数就是可读事件数*/
  165.             {
  166.                     if(evs[i].data.fd == sockfd) /*sockfd有事件产生*/
  167.                     {
  168.                             if (evs[i].events & EPOLLIN) /*监听套接字sockfd可读:表示sockfd已经接受客户端的连接请求*/
  169.                             {
  170.                                     printf("有客户端已经连接:可以用accept获得连接请求并建立连接!\n");
  171.                                     len = sizeof(cliaddr); /*-结果参数*/
  172.                                     /*if((newfd=accept(sockfd,(struct sockaddr *)&cliaddr,(socklen_t *)&len))==-1)*/
  173.                                     if((newfd=accept(sockfd, (struct sockaddr *)NULL, (socklen_t *)NULL)) == -1) /*不关心客户端标识*/
  174.                          {
  175.                          perror("accept() failed");
  176.                          exit(1);
  177.                          }
  178.                                  struct sockaddr_in serv;
  179.                                  struct sockaddr_in guest;
  180.                                  socklen_t serv_len = sizeof(serv);
  181.                                  socklen_t guest_len = sizeof(guest);
  182.                                 
  183.                                  getsockname(newfd, (struct sockaddr *)&serv, &guest_len);
  184.                                  getpeername(newfd, (struct sockaddr *)&guest, &serv_len);
  185.                                 
  186.                                  inet_ntop(AF_INET, &guest.sin_addr.s_addr, guest_ip, sizeof(guest_ip));
  187.                                  inet_ntop(AF_INET, &serv.sin_addr.s_addr, serv_ip, sizeof(serv_ip));
  188.                                  printf("服务端IP和PORT[%s]:[%d]\n", serv_ip, ntohs(serv.sin_port));
  189.                                  printf("客户端IP和PORT[%s]:[%d]\n", guest_ip, ntohs(guest.sin_port));
  190.                                 
  191.                                     ev.data.fd = newfd;
  192.                                     ev.events = EPOLLIN;
  193.                                     epoll_ctl(epfd, EPOLL_CTL_ADD, newfd, &ev); /*将连接套接字newfd的可读事件,也注册到epfd中*/
  194.                                     printf("ev.data.fd newfd=[%d]\n", ev.data.fd);
  195.                             }
  196.                     }
  197.             else /*其他fd有事件产生*/
  198.             {
  199.                             printf("服务器读取套接字[%d]的内容:\n", evs[i].data.fd);
  200.                             memset(readbuf, 0, sizeof(readbuf));
  201.                             ret = read(evs[i].data.fd, readbuf, sizeof(readbuf));
  202.                             printf("read=[%s] ret = [%d]\n", readbuf, ret);
  203.                             for(j=0; j<ret; j++)
  204.                             {
  205.                                     printf("***[%02x]***\n", readbuf[j]);
  206.                             }
  207.                             
  208.                             printf("服务器写入套接字[%d]的内容:\n", evs[i].data.fd);
  209.                             ret = write(evs[i].data.fd, readbuf, strlen(readbuf));
  210.                             printf("write=[%s] ret = [%d]\n", readbuf, ret);
  211.             }
  212.         }
  213.     }
  214.     close(sockfd);
  215.     return 0;
  216. }
  217. /*这个程序为什么不能有多个客户端同时连接?*/

  218. /*2个服务器,不能同时bind一个端口*/

阅读(428) | 评论(0) | 转发(0) |
0

上一篇:socket服务端

下一篇:getaddrinfo函数

给主人留下些什么吧!~~