비차단 socket 호출connect, epoll,select 검사 연결 상황 예시

19856 단어 connect
우리는 linux에서 socket 프로그래밍을 하면 흔히 볼 수 있는 몇 가지 시스템 호출이 있다는 것을 안다.
서버에는 socket(),bind(),listen(),accept(),read(),write()
클라이언트에게 socket (), connect () 가 있습니다
여기서 주로 말하고자 하는 것은 클라이언트 이쪽의connect 함수입니다.
클라이언트의 경우 소켓을 열고 대쪽 서버에 연결해야 합니다. 예를 들어
 1 int main(int argc, char **argv) 

 2 {

 3         struct sockaddr_in s_addr;

 4         memset(&s_addr, 0, sizeof(s_addr));

 5         s_addr.sin_family = AF_INET;

 6         s_addr.sin_addr.s_addr = inet_addr("remote host");

 7         s_addr.sin_port = htons(remote port);

 8         socklen_t addr_len = sizeof(struct sockaddr);

 9         int c_fd = socket(AF_INET, SOCK_STREAM, 0);

10         int ret = connect(c_fd, (struct sockaddr*)&s_addr, addr_len);                                 

11         ......

12 }

 
연결에 있는 인터페이스 서버를 사용하면 이 플러그인으로 데이터를 보낼 수 있습니다.
우리는 만약에 socket가 TCP 플러그인이라면connect 함수는 TCP의 세 번의 악수 과정을 자극하고, 세 번의 악수는 약간의 시간을 필요로 한다. 내부 핵에서connect에 대한 시간 초과 제한은 75초이다. 즉, 75초를 초과하면connect는 시간 초과로 인해 실패로 돌아간다는 것이다.그러나 만약에 인터페이스 서버가 어떤 문제로 연결할 수 없다면, 모든 클라이언트가 시작한 연결은 75를 기다려야 되돌아옵니다. 왜냐하면 socket은 기본적으로 막혀 있기 때문입니다.일부 온라인 서비스에 대해 말하자면, 일부 터미널 서버에 문제가 생겼다고 가정하면 이런 상황에서 심각한 결과를 초래할 수 있다.또는, 때때로, 우리는connect를 호출할 때 막히기를 원하지 않으며, 일부 추가 작업은 처리해야 한다.
이런 장면에서 우리는 socket를 비차단으로 설정할 수 있다. 다음과 같은 코드이다.
int flags = fcntl(c_fd, F_GETFL, 0);

if(flags < 0) {

    return 0;      

}

fcntl(c_fd, F_SETFL, flags | O_NONBLOCK);

우리가 socket을 NONBLOCK으로 설정한 후,connect를 호출할 때, 작업이 바로 완료되지 않으면,connect는 즉시 되돌아옵니다. 이때connect는 -1로 되돌아갈 수 있습니다. 이때 해당하는 오류코드errno에 따라 연결이 계속 진행되고 있는지 판단해야 합니다.
errno=EINPROGRESS일 때 이런 상황은 정상이고 이때 연결은 계속 진행되고 있지만 아직 완성되지 않았다.동시에 TCP의 삼중 악수 조작은 계속 진행된다.다음은select/epoll로 대응하는 이벤트를 등록하고 시간 초과를 설정하여 연결이 성공했는지 판단하면 됩니다.
int ret = connect(c_fd, (struct sockaddr*)&s_addr, addr_len);

while(ret < 0) {

    if( errno == EINPROGRESS ) {

         break;

    }  else {

         perror("connect fail'
"); return 0; } }

이 곳에서 우리는 만약ret가 0보다 작으면 연결 실패를 직접 판단하고 돌아왔다고 판단할 가능성이 높다. errno에 따라 EINPROGRESS라는 오류 코드를 판단하지 않았다.여기도 어제 프로그램을 쓸 때 만났던 구덩이야.
비차단connect를 사용할 때 주의해야 할 문제는: 1.연결을 호출할 때 바로 연결을 만들 수 있습니다. (예를 들어 클라이언트와 서버가 같은 컴퓨터에 있는 경우) 이 상황을 처리해야 합니다.2. Posix는 select와 비저지connect와 관련된 두 가지 규정을 정의했다. 1) 연결이 성공적으로 구축되었을 때 socket 설명자는 쓸 수 있게 되었다.(연결이 설정되었을 때 쓰기 버퍼가 비어 쓰기 가능) 2) 연결이 실패했을 때 socket 설명자는 읽을 수도 있고 쓸 수도 있습니다.(미결 오류로 인해 읽을 수 있고 쓸 수 있음)
그러나 나는 epoll로도 실험을 했다. (connect는 무효 포트, errno=110, errmsg=connectrefused) 연결이 실패할 때 epoll의 EPOLLERR와 EPOLLLIN을 촉발하고 EPOLLOUT를 촉발하지 않는다.
select로 연결을 검사할 때, socket은 읽을 수도 있고 쓸 수도 있으며, 읽을 수 있는 집합에서만 getsockopt를 통해 오류 코드를 얻을 수 있습니다.
epoll로 연결을 검사할 때, socket은 읽을 수도 있고 쓸 수도 있으며, EPOLLERR에서 getsockopt를 통해서만 오류 코드를 얻을 수 있습니다.
전체 코드는 다음과 같습니다.
/* 

 * File:   main.cpp

 * Created on March 7, 2013, 5:54 PM

 */



#include <cstdlib>

#include <string>

#include <iostream>



#include <sys/epoll.h>

#include <sys/socket.h>

#include <sys/types.h>

#include <sys/select.h>

#include <error.h>

#include <errno.h>

#include <fcntl.h>

#include <netinet/in.h>

#include <arpa/inet.h>





using namespace std;



struct so {

    int fd;

    string val;

};



int select_version(int *fd) {

    int c_fd = *fd;

    fd_set rset, wset;

    struct timeval tval;

    FD_ZERO(&rset);

    FD_SET(c_fd, &rset);

    wset = rset;

    tval.tv_sec = 0;

    tval.tv_usec = 300 * 1000; //300 

    int ready_n;

    if ((ready_n = select(c_fd + 1, &rset, &wset, NULL, &tval)) == 0) {

        close(c_fd); /* timeout */

        errno = ETIMEDOUT;

        perror("select timeout.
"); return (-1); } if (FD_ISSET(c_fd, &rset)) { int error; socklen_t len = sizeof (error); if (getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { cout << "getsockopt error." << endl; return -1; } cout << "in fire." << error << endl; } if (FD_ISSET(c_fd, &wset)) { int error; socklen_t len = sizeof (error); if (getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { cout << "getsockopt error." << endl; return -1; } cout << "out fire." << error << endl; } return 0; } int epoll_version(int *fd) { int c_fd = *fd; int ep = epoll_create(1024); struct epoll_event event; event.events = (uint32_t) (EPOLLIN | EPOLLOUT | EPOLLET); struct so _data; _data.fd = c_fd; _data.val = "test"; event.data.ptr = (void*) &_data; epoll_ctl(ep, EPOLL_CTL_ADD, c_fd, &event); struct epoll_event eventArr[1000]; int status, err; socklen_t len; err = 0; len = sizeof (err); int n = epoll_wait(ep, eventArr, 20, 300); for (int i = 0; i < n; i++) { epoll_event ev = eventArr[i]; int events = ev.events; if (events & EPOLLERR) { struct so* so_data = (struct so*) ev.data.ptr; cout << so_data->val << ",err event fire." << endl; status = getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &err, &len); cout << status << "," << err << endl; } if (events & EPOLLIN) { struct so* so_data = (struct so*) ev.data.ptr; cout << so_data->val << ",in event fire." << endl; status = getsockopt(c_fd, SOL_SOCKET, SO_ERROR, &err, &len); cout << status << "," << err << endl; } if (events & EPOLLOUT) { struct so* so_data1 = (struct so*) ev.data.ptr; cout << so_data1->val << ",out event fire." << endl; } } } int main(int argc, char** argv) { string ip = "127.0.0.1"; int port = 25698; int c_fd, flags, ret; struct sockaddr_in s_addr; memset(&s_addr, 0, sizeof (s_addr)); s_addr.sin_family = AF_INET; s_addr.sin_port = htons(port); s_addr.sin_addr.s_addr = inet_addr(ip.c_str()); if ((c_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("create socket fail.
"); exit(0); } flags = fcntl(c_fd, F_GETFL, 0); if (flags < 0) { perror("get socket flags fail.
"); return -1; } if (fcntl(c_fd, F_SETFL, flags | O_NONBLOCK) < 0) { perror("set socket O_NONBLOCK fail.
"); return -1; } ret = connect(c_fd, (struct sockaddr*) &s_addr, sizeof (struct sockaddr)); while (ret < 0) { if (errno == EINPROGRESS) { break; } else { perror("connect remote server fail.
"); printf("%d
", errno); exit(0); } } //select_version(&c_fd); epoll_version(&c_fd); return 0; }

 
ps:문장에 타당하지 않은 부분이 있으면 박우 여러분의 지적을 바랍니다.

좋은 웹페이지 즐겨찾기