libuv 학습 노트 5------TCP 클라이언트의 실현

4572 단어
앞에서 우리는 어떻게 libuv로 하나의 TCP 서버를 실현하는지 이야기했다. libuv로 하나의 클라이언트를 실현하는 것은libuv로 하나의 TCP 서버를 실현하는 것과 매우 유사하다.다른 점은 uv_를 진행할 필요가 없다는 것이다tcp_bind 작업, uv_listen에서 uv_로 변경tcp_connect.
TCP 클라이언트의 기본 단계는 다음과 같습니다.
1. uv_tcp_init tcp 핸들 만들기
2.uv_tcp_connect tcp 연결 만들기
3.stream 조작을 사용하여 클라이언트와 통신하기
사용된 API는 다음과 같습니다.
1.uv_tcp_init
2.uv_ip4_addr
3.uv_tcp_connect
4.uv_write/uv_read_start
TCP 클라이언트의 구현만을 위한 새로운 API 함수 uv_ 소개tcp_connect.

1.uv_tcp_connect 함수 설명


int uv_tcp_connect(uv_connect_t* req,uv_tcp_t* handle,const struct sockaddr* addr,uv_connect_cb cb);
매개변수 1: 요청 객체 연결
매개변수 2: TCP 클라이언트 객체
매개 변수 3: 채워진structsockaddr_구조체
매개변수 4: 콜백 함수
struct sockaddr_in addr;

uv_connect_t* connect = (uv_connect_t*)malloc(sizeof(uv_connect_t));


uv_ip4_addr("192.168.65.205",DEFAULT_PORT,&addr);

int r = uv_tcp_connect(connect,&mysocket,(const struct sockaddr*)&addr,on_connect);

if(r)
{
    fprintf(stderr, "connect error %s
", uv_strerror(r)); return 1; }

2.uv_tcp_connect 리셋 함수 소개


void (*uv_connect_cb)(uv_connect_t* req, int status);
연결 성공/실패 후 이 함수를 호출합니다.
status: 되돌아오는 상태입니다. 0보다 작으면 오류가 발생합니다.
req: 연결 요청 대상, req->handle는 TCP 클라이언트 대상(mysocket)을 가리키며, req->handle를 uv_에 직접 사용할 수 있습니다write,uv_read_start 등류 작업 중입니다.

3. 지정한 파일을 서버에 전송하는 코드 구현

#include 
#include 
#include 

uv_loop_t *loop;
#define DEFAULT_PORT 7000

uv_tcp_t mysocket;

char *path = NULL;
uv_buf_t iov;
char buffer[128];

uv_fs_t read_req;
uv_fs_t open_req;
void on_read(uv_fs_t *req);
void on_write(uv_write_t* req, int status)
{
    if (status < 0) 
    {
        fprintf(stderr, "Write error: %s
", uv_strerror(status)); uv_fs_t close_req; uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL); uv_close((uv_handle_t *)&mysocket,NULL); exit(-1); } else { uv_fs_read(uv_default_loop(), &read_req, open_req.result, &iov, 1, -1, on_read); } } void on_read(uv_fs_t *req) { if (req->result < 0) { fprintf(stderr, "Read error: %s
", uv_strerror(req->result)); } else if (req->result == 0) { uv_fs_t close_req; // synchronous uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL); uv_close((uv_handle_t *)&mysocket,NULL); } else { iov.len = req->result; uv_write((uv_write_t *)req,(uv_stream_t *)&mysocket,&iov,1,on_write); } } void on_open(uv_fs_t *req) { if (req->result >= 0) { iov = uv_buf_init(buffer, sizeof(buffer)); uv_fs_read(uv_default_loop(), &read_req, req->result,&iov, 1, -1, on_read); } else { fprintf(stderr, "error opening file: %s
", uv_strerror((int)req->result)); uv_close((uv_handle_t *)&mysocket,NULL); exit(-1); } } void on_connect(uv_connect_t* req, int status) { if(status < 0) { fprintf(stderr,"Connection error %s
",uv_strerror(status)); return; } fprintf(stdout,"Connect ok
"); uv_fs_open(loop,&open_req,path,O_RDONLY,-1,on_open); } int main(int argc,char **argv) { if(argc < 2) { fprintf(stderr,"Invaild argument!
"); exit(1); } loop = uv_default_loop(); path = argv[1]; uv_tcp_init(loop,&mysocket); struct sockaddr_in addr; uv_connect_t* connect = (uv_connect_t*)malloc(sizeof(uv_connect_t)); uv_ip4_addr("127.0.0.1",DEFAULT_PORT,&addr); int r = uv_tcp_connect(connect,&mysocket,(const struct sockaddr *)&addr,on_connect); if(r) { fprintf(stderr, "connect error %s
", uv_strerror(r)); return 1; } return uv_run(loop,UV_RUN_DEFAULT); }

이 코드는 libuv 파일 조작, 흐름 조작, TCP 실현 등 지식을 이용했다.코드의 기본 논리는 다음과 같습니다.
1. TCP 클라이언트 객체와 TCP 연결 요청 객체를 만들고 서버와 연결합니다.
2. 연결에 성공하면 전송할 파일을 엽니다.
3. 파일을 열면 일정한 파일 내용을 읽고 읽으면 파일 내용을stream 조작을 이용하여 서버에 보냅니다.
4. 전송에 성공하면 파일 내용을 다시 읽고 읽기에 성공하면 서버에 보냅니다.
5. 파일 끝까지 읽을 때까지 4단계로 반복합니다.
6. 파일을 닫고 흐름을 닫습니다.
 

테스트:


1. 서버를 열고 7000 포트를 감청하고 출력을 파일로 바꿉니다.
nc -l 7000 > test_recv.jpg
2. 프로그램을 컴파일하고 실행합니다.
gcc main.c -luv
./a.out test.jpg

좋은 웹페이지 즐겨찾기