비차단 socket 호출connect, epoll,select 검사 연결 상황 예시
19856 단어 connect
서버에는 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:문장에 타당하지 않은 부분이 있으면 박우 여러분의 지적을 바랍니다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Dr.Sum Connect를 이용한 월초, 월말 취득 방법 [ETL은 거의 동일][자세한 내용은 여기 있습니다] Dr.Sum은 1초에 10억 개의 데이터를 계산할 수 있는 초고속 데이터베이스 도구입니다. 또 Dr.Sum은 합계 속도뿐 아니라 데이터베이스 구축 경험이 적은 사람도 GUI 조작으로 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.