#include #include #include #include #include #include #include #include #include #include #include #include #include #include #define READ_BUF_SIZE 10000 #define MAX_DATAGRAM_SIZE 1200 #define MAX_BUFFER_SIZE 10000 struct simple_client { struct quic_endpoint_t *quic_endpoint; ev_timer timer; int sock; struct sockaddr_storage local_addr; socklen_t local_addr_len; struct quic_tls_config_t *tls_config; struct quic_conn_t *conn; struct ev_loop *loop; }; char buf_stdin[MAX_BUFFER_SIZE] = {"\0"}; int buf_stdin_cnt = 0; struct quic_conn_t* conn_t = NULL; void client_on_conn_created(void *tctx, struct quic_conn_t *conn) { struct simple_client *client = tctx; client->conn = conn; conn_t = conn; } void client_on_conn_established(void *tctx, struct quic_conn_t *conn) { printf("connect established!\n"); } void client_on_conn_closed(void *tctx, struct quic_conn_t *conn) { fprintf(stderr, "connection closed\n"); struct simple_client *client = tctx; free(conn_t); close(client->sock); ev_break(client->loop, EVBREAK_ALL); } void client_on_stream_created(void *tctx, struct quic_conn_t *conn, uint64_t stream_id) { fprintf(stderr, "stream %ld created\n", stream_id); } void client_on_stream_readable(void *tctx, struct quic_conn_t *conn, uint64_t stream_id) { struct simple_client *client = tctx; char buf_to_app[MAX_BUFFER_SIZE]; bool fin = false; ssize_t readnum = quic_stream_read(conn, stream_id, buf_to_app, MAX_BUFFER_SIZE, &fin); if (readnum < 0) { fprintf(stderr, "stream[%ld] read error\n", stream_id); return; } printf("readnum = %ld\n", readnum); if(fin){ printf("read msg:%.*s\n", (int)readnum, buf_to_app); } } void client_on_stream_writable(void *tctx, struct quic_conn_t *conn, uint64_t stream_id) { printf("stream %ld Writable!\n",stream_id); printf("msg to be writed is %.*s\n", buf_stdin_cnt, buf_stdin); ssize_t w = quic_stream_write(conn_t, stream_id, buf_stdin, buf_stdin_cnt, true); if(w < 0){ printf("w = %zd, failed to write!\n", w); return; } printf("write %zd bytes\n", w); memset(buf_stdin, 0, MAX_BUFFER_SIZE); buf_stdin_cnt = 0; } void client_on_stream_closed(void *tctx, struct quic_conn_t *conn, uint64_t stream_id) { fprintf(stderr, "stream closed %ld\n", stream_id); } int client_on_packets_send(void *psctx, struct quic_packet_out_spec_t *pkts, unsigned int count) { struct simple_client *client = psctx; unsigned int sent_count = 0; int i, j = 0; for (i = 0; i < count; i++) { struct quic_packet_out_spec_t *pkt = pkts + i; for (j = 0; j < (*pkt).iovlen; j++) { const struct iovec *iov = pkt->iov + j; ssize_t sent = sendto(client->sock, iov->iov_base, iov->iov_len, 0, (struct sockaddr *)pkt->dst_addr, pkt->dst_addr_len); if (sent != iov->iov_len) { if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) { fprintf(stderr, "send would block, already sent: %d\n", sent_count); return sent_count; } return -1; } sent_count++; } } return sent_count; } struct quic_transport_methods_t quic_transport_methods = { .on_conn_created = client_on_conn_created, .on_conn_established = client_on_conn_established, .on_conn_closed = client_on_conn_closed, .on_stream_created = client_on_stream_created, .on_stream_readable = client_on_stream_readable, .on_stream_writable = client_on_stream_writable, .on_stream_closed = client_on_stream_closed, }; const struct quic_packet_send_methods_t quic_packet_send_methods = { .on_packets_send = client_on_packets_send, }; static void process_connections(struct simple_client *client) { quic_endpoint_process_connections(client->quic_endpoint); double timeout = quic_endpoint_timeout(client->quic_endpoint) / 1e3f; if (timeout < 0.0001) { timeout = 0.0001; } printf("timeout = %lf\n", timeout); client->timer.repeat = timeout; ev_timer_again(client->loop, &client->timer); } static void read_callback(EV_P_ ev_io *w, int revents) { struct simple_client *client = w->data; static uint8_t bufrec[READ_BUF_SIZE]; fprintf(stderr, "ev readcb\n"); while (true) { struct sockaddr_storage peer_addr; socklen_t peer_addr_len = sizeof(peer_addr); memset(&peer_addr, 0, peer_addr_len); ssize_t read = recvfrom(client->sock, bufrec, sizeof(bufrec), 0, (struct sockaddr *)&peer_addr, &peer_addr_len); if (read < 0) { if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) { break; } fprintf(stderr, "failed to read\n"); return; } quic_packet_info_t quic_packet_info = { .src = (struct sockaddr *)&peer_addr, .src_len = peer_addr_len, .dst = (struct sockaddr *)&client->local_addr, .dst_len = client->local_addr_len, }; int r = quic_endpoint_recv(client->quic_endpoint, bufrec, read, &quic_packet_info); if (r != 0) { fprintf(stderr, "recv failed %d\n", r); continue; } } process_connections(client); } static void timeout_callback(EV_P_ ev_timer *w, int revents) { struct simple_client *client = w->data; quic_endpoint_on_timeout(client->quic_endpoint); process_connections(client); } static void debug_log(const uint8_t *data, size_t data_len, void *argp) { fwrite(data, sizeof(uint8_t), data_len, stderr); } static int create_socket(const char *host, const char *port, struct addrinfo **peer, struct simple_client *client) { const struct addrinfo hints = {.ai_family = PF_UNSPEC, .ai_socktype = SOCK_DGRAM, .ai_protocol = IPPROTO_UDP}; if (getaddrinfo(host, port, &hints, peer) != 0) { fprintf(stderr, "failed to resolve host\n"); return -1; } int sock = socket((*peer)->ai_family, SOCK_DGRAM, 0); if (sock < 0) { fprintf(stderr, "failed to create socket\n"); return -1; } if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) { fprintf(stderr, "failed to make socket non-blocking\n"); return -1; } client->local_addr_len = sizeof(client->local_addr); if (getsockname(sock, (struct sockaddr *)&client->local_addr, &client->local_addr_len) != 0) { fprintf(stderr, "failed to get local address of socket\n"); return -1; }; client->sock = sock; return 0; } void std_in_callback(EV_P_ ev_io *w, int revents){ struct simple_client *client = w->data; uint64_t sid; //读取stdin数据并存入缓存 buf_stdin_cnt = read(w->fd, buf_stdin, MAX_BUFFER_SIZE); buf_stdin_cnt--; //创建流 int result = quic_stream_uni_new(conn_t, 10, false, &sid); if(result < 0){ printf("stdin create stream error = %d\n", result); } quic_endpoint_process_connections(client->quic_endpoint); } void setStdinNonBlocking(void) { int flags = fcntl(STDIN_FILENO, F_GETFL, 0); // 获取当前flags if (flags == -1) { perror("fcntl - GETFL"); exit(EXIT_FAILURE); } if (fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK) == -1) { // 设置flags perror("fcntl - SETFL"); exit(EXIT_FAILURE); } } int main(int argc, char *argv[]) { if (argc < 3) { fprintf(stderr, "%s \n", argv[0]); return -1; } // Set logger./ quic_set_logger(debug_log, NULL, "INFO"); // Create client. struct simple_client client; client.quic_endpoint = NULL; client.tls_config = NULL; client.conn = NULL; client.loop = NULL; quic_config_t *config = NULL; int ret = 0; // Create socket. const char *host = argv[1]; const char *port = argv[2]; struct addrinfo *peer = NULL; if (create_socket(host, port, &peer, &client) != 0) { ret = -1; goto EXIT; } // Create quic config. config = quic_config_new(); if (config == NULL) { fprintf(stderr, "failed to create config\n"); ret = -1; goto EXIT; } quic_config_set_max_idle_timeout(config, 50000); quic_config_set_recv_udp_payload_size(config, MAX_DATAGRAM_SIZE); // Create and set tls config. const char *const protos[1] = {"http/0.9"}; client.tls_config = quic_tls_config_new_client_config(protos, 1, true); if (client.tls_config == NULL) { ret = -1; goto EXIT; } quic_config_set_tls_config(config, client.tls_config); // Create quic endpoint client.quic_endpoint = quic_endpoint_new(config, false, &quic_transport_methods, &client, &quic_packet_send_methods, &client); if (client.quic_endpoint == NULL) { fprintf(stderr, "failed to create quic endpoint\n"); ret = -1; goto EXIT; } setStdinNonBlocking(); // Init event loop. client.loop = ev_default_loop(0); ev_init(&client.timer, timeout_callback); client.timer.data = &client; // Connect to server. ret = quic_endpoint_connect( client.quic_endpoint, (struct sockaddr *)&client.local_addr, client.local_addr_len, peer->ai_addr, peer->ai_addrlen, NULL /* server_name */, NULL /* session */, 0 /* session_len */, NULL /* token */, 0 /* token_len */, NULL /* config */, NULL /* index */); if (ret < 0) { fprintf(stderr, "failed to connect to client: %d\n", ret); ret = -1; goto EXIT; } process_connections(&client); // Start event loop. ev_io watcher; ev_io_init(&watcher, read_callback, client.sock, EV_READ); ev_io_start(client.loop, &watcher); watcher.data = &client; ev_io stdin_watcher; ev_io_init(&stdin_watcher, std_in_callback, 0, EV_READ); ev_io_start(client.loop, &stdin_watcher); stdin_watcher.data = &client; ev_loop(client.loop, 0); EXIT: if (peer != NULL) { freeaddrinfo(peer); } if (client.tls_config != NULL) { quic_tls_config_free(client.tls_config); } if (client.sock > 0) { close(client.sock); } if (client.quic_endpoint != NULL) { quic_endpoint_free(client.quic_endpoint); } if (client.loop != NULL) { ev_loop_destroy(client.loop); } if (config != NULL) { quic_config_free(config); } return ret; }