顯示具有 socket 標籤的文章。 顯示所有文章
顯示具有 socket 標籤的文章。 顯示所有文章

2012/08/01

Ignore SIGPIPE in socket programming

通常在寫 socket programming 時,系統對一個 invalid fd (file descriptor) 做寫入時會讓系統丟出 SIGPIPE 的 signal,為了避免程式被中斷,通常都會 ignore 它。

#include <signal.h>
struct sigaction sa;

sa.sa_handler = SIG_IGN;
sa.sa_flags = 0;

sigemptyset(&sa.sa_mask);
sigaction(SIGPIPE, &sa, 0);

2011/07/12

在iPhone上建立HTTP連線(GET/POST)

HTTP Get
Create request
NSString *str = @"http://my_server/get.php?name=brian&gender=male";
NSString *str_enc = [str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [[NSURL URLWithString:str_enc] retain];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];

Prepare delegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;


HTTP Post
NSString *content = @"name=brian&gender=male";
NSData *postData = [content dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:@"%d", [postData length]];
 
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:@"http://my_server/post.php"]];
[request setHTTPMethod:@"POST"];

/* Header */
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];

/* Content */
[request setHTTPBody:postData];

connect = [[NSURLConnection alloc] initWithRequest:request delegate:self];
// 跟GET一樣設定4組delegate去接response

參考資料

2011/06/20

HTTP server over libevent - sample code (2)

加入一些parse的function,可以得到request的method、URI、header、body、path

#include 
#include 
#include 
#include 
#include 

#define SVR_IP                         "127.0.0.1"
#define SVR_PORT                       8080

void dump_cb (struct evhttp_request *req, void *arg) {
    const char        *uri;
    struct evkeyvalq  *headers;
    struct evkeyval   *header;
    struct evbuffer   *evbuf;
    struct evhttp_uri *ev_uri;
    
    /* Get method */
    switch (evhttp_request_get_command(req)) {
        case EVHTTP_REQ_GET: 
            printf("Request method=[GET]\n");
            break;
        case EVHTTP_REQ_POST:
            printf("Request method=[POST]\n");
            break;
        default:
            printf("Request method=[others]\n");
            break;
    }
    
    /* Get path */
    uri = evhttp_request_get_uri(req);
    printf("Request uri=[%s]\n",  uri);
    
    /* Get header */
    printf("Headers:\n");
    headers = evhttp_request_get_input_headers(req);
    for (header = headers->tqh_first; header;
        header = header->next.tqe_next) {
        printf("  %s: %s\n", header->key, header->value);
    }
        
    /* Get body */
    evbuf = evhttp_request_get_input_buffer(req);
    printf("Body:\n");
    while (evbuffer_get_length(evbuf)) {
        int  size;
        char buf[128];
        
        memset(buf, 0, sizeof(buf));
        size = evbuffer_remove(evbuf, buf, sizeof(buf) - 1);
        printf("  %s\n", buf);
    }
    
    /* */
    ev_uri = evhttp_uri_parse(uri);
    if (ev_uri) {
        const char *path;
        char       *decoded_path;
        
        path = evhttp_uri_get_path(ev_uri);
        decoded_path = evhttp_uridecode(path, 0, NULL);
        printf("Request path=[%s]\n", decoded_path);
        
        /* Free resource */
        free(decoded_path);
        evhttp_uri_free(ev_uri);
    }
    
    /* Response */
    evhttp_send_reply(req, HTTP_OK, "OK", NULL);
}

int main (int argc, char **argv) {
    struct event_base *evbase;
    struct evhttp     *evhttp;

    /* Init event base */
    if ((evbase = event_base_new()) == NULL) {
        printf("event_base_new() failed\n");
        return -1;
    }

    /* Init evhttp */
    if ((evhttp = evhttp_new(evbase)) == NULL) {
        printf("evhttp_new() failed\n");
        return -1;
    }

    /* Set server IP, port */
    if (evhttp_bind_socket(evhttp, SVR_IP, SVR_PORT) == -1) {
        printf("evhttp_bind_socket() failed\n");
        return -1;
    } else {
        printf("Listening on [%s:%d]\n", SVR_IP, SVR_PORT);
    }
    
    /* Set timeout */
    evhttp_set_timeout(evhttp, 10);
    
    /* Set a callback for default path */
    evhttp_set_gencb(evhttp, dump_cb, NULL);

    /* Enter event loop */
    event_base_dispatch(evbase);

    /* Free resource */
    evhttp_free(evhttp);
    event_base_free(evbase);
    
    return 0;
}



Compile and run
$ gcc -g -Wall -o httpd httpd.c -levent -I/opt/libevent-2.0.12-stable/include/ -L/opt/libevent-2.0.12-stable/lib/
$ LD_LIBRARY_PATH=/opt/libevent-2.0.12-stable/lib ./httpd
Listening on [127.0.0.1:8080]

Request method=[GET]
Request uri=[/test.php?name=kevin&sex=male]
Headers:
  User-Agent: Wget/1.11.4 Red Hat modified
  Accept: */*
  Host: 127.0.0.1:8080
  Connection: Keep-Alive
Body:
Request path=[/test.php]

Request method=[POST]
Request uri=[/test.php]
Headers:
  User-Agent: Wget/1.11.4 Red Hat modified
  Accept: */*
  Host: 127.0.0.1:8080
  Connection: Keep-Alive
  Content-Type: application/x-www-form-urlencoded
  Content-Length: 19
Body:
  name=sylar&sex=male
Request path=[/test.php]


try to send a request to server...
# Get
$wget -q -O - 'http://127.0.0.1:8080/test.php?name=sylar&sex=male'

# Post
$wget -q -O - --post-data='name=sylar&sex=male' http://127.0.0.1:8080/test.php

2011/06/17

HTTP server over libevent - sample code (1)

使用libevent HTTP API寫一個簡易的server。

#include 
#include 
#include 

#define SVR_IP                         "127.0.0.1"
#define SVR_PORT                       8080

void specific_cb (struct evhttp_request *req, void *arg) {
    struct evbuffer *evbuf;
    
    if ((evbuf = evbuffer_new()) == NULL) {
        printf("evbuffer_new() failed");
        evhttp_send_reply(req, HTTP_INTERNAL, "Internal error", NULL);
        return;
    }

    /* Body */
    evbuffer_add_printf(evbuf, "It's work!");
    
    /* Response */
    evhttp_send_reply(req, HTTP_OK, "OK", evbuf);
    
    /* Free resource */
    evbuffer_free(evbuf);
}

void generic_cb (struct evhttp_request *req, void *arg) {

    /* Response */
    evhttp_send_reply(req, HTTP_NOTFOUND, "Not found", NULL);
}

int main (int argc, char **argv) {
    struct event_base *evbase;
    struct evhttp     *evhttp;

    /* Init event base */
    if ((evbase = event_base_new()) == NULL) {
        printf("event_base_new() failed\n");
        return -1;
    }

    /* Init evhttp */
    if ((evhttp = evhttp_new(evbase)) == NULL) {
        printf("evhttp_new() failed\n");
        return -1;
    }

    /* Set server IP, port */
    if (evhttp_bind_socket(evhttp, SVR_IP, SVR_PORT) == -1) {
        printf("evhttp_bind_socket() failed\n");
        return -1;
    } else {
        printf("Listening on [%s:%d]\n", SVR_IP, SVR_PORT);
    }
    
    /* Set a callback for specific path */
    if (evhttp_set_cb(evhttp, "/test", specific_cb, NULL) < 0) {
        printf("evhttp_set_cb() failed\n");
        return -1;
    }
    
    /* Set a callback for default path */
    evhttp_set_gencb(evhttp, generic_cb, NULL);

    /* Enter event loop */
    event_base_dispatch(evbase);

    /* Free resource */
    evhttp_free(evhttp);
    event_base_free(evbase);
    
    return 0;
}
Compile and run
$ gcc -g -Wall -o httpd httpd.c -levent -I/opt/libevent-2.0.12-stable/include/ -L/opt/libevent-2.0.12-stable/lib/
$ LD_LIBRARY_PATH=/opt/libevent-2.0.12-stable/lib ./httpd
Listening on [127.0.0.1:8080]
try to send a request to server...
$ wget -q -O - http://127.0.0.1:8080/test
$ wget -S -O - http://127.0.0.1:8080/xxx
不知道為什麼我用2.0.10-stable版本跑會有下列warning,用2.0.12-stable就沒問題了。
[warn] evhttp_connection_base_new: bufferevent_new failed: No such file or directory
[warn] evhttp_get_request: cannot get connection on 9: No such file or directory

2011/06/16

C socket client in Linux - sample code

Sample code

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define LOCAL_IP                        "127.0.0.1"
#define LOCAL_PORT                      5678
#define SVR_IP                          "127.0.0.1"
#define SVR_PORT                        8080
#define BUF_SIZE                        1024
#define MESSAGE                         "hello"

int main (int argc, char **argv) {
    struct sockaddr_in  local_addr, server_addr;
    socklen_t           len;
    int                 sock_fd;
    char                buff[BUF_SIZE];
    int                 recv_len;
    
    len = sizeof(struct sockaddr_in);
    
    /* Set local address (In general, we don't care local address) */
    memset(&local_addr, 0, sizeof(local_addr));
    local_addr.sin_family = AF_INET;
    local_addr.sin_addr.s_addr = inet_addr(LOCAL_IP);
    local_addr.sin_port = htons(LOCAL_PORT);
    
    /* Set server address */
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(SVR_IP);
    server_addr.sin_port = htons(SVR_PORT);

    /* Create endpoint */
    if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket()");
        return -1;
    } else {
        printf("sock_fd=[%d]\n", sock_fd);
    }

    /* Bind */
    if (bind(sock_fd, (struct sockaddr *)&local_addr, sizeof(local_addr)) == -1) {
        perror("bind()");
        return -1;
    } else {
        printf("bind [%s:%u] success\n",
            inet_ntoa(local_addr.sin_addr), ntohs(local_addr.sin_port));
    }

    /* Connect */
    if (connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("connect()");
        return -1;
    } else {
        printf("connect to [%s:%u] success\n",
            inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port));
    }
    
    /* Send */
    send(sock_fd, MESSAGE, strlen(MESSAGE), 0);

    /* Receive */
    memset(buff, 0, sizeof(buff));
    recv_len = recv(sock_fd, buff, sizeof(buff), 0);
    if (recv_len == -1) {
        perror("recv()");
        return -1;
    } else if (recv_len == 0) {
        printf("Client disconnect\n");
    } else {
        printf("Receive: len=[%d] msg=[%s]\n", recv_len, buff);
    }
    
    return 0;
}

2011/06/15

C socket server in Linux - sample code

長期用libevent來implement socket program,都快忘了一般select的用法了,寫了一個echo server
供各位參考。

不過有一點我還是不太懂,為何fd_set要copy一份起來,是因為fd_set 經過select function後就無效了嗎?我試過只用一個fd_set,果真只能接收到第一個request,除非每次select前重新把所有的fd做FD_SET()。

Select的用法可以參考:石頭閒語:select() - I/O Multiplexer

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SVR_IP                          "127.0.0.1"
#define SVR_PORT                        8080
#define BUF_SIZE                        1024

int main (int argc, char **argv) {
    struct sockaddr_in  server_addr;
    socklen_t           len;
    fd_set              active_fd_set;
    int                 sock_fd;
    int                 max_fd;
    int                 flag = 1;
    char                buff[BUF_SIZE];
    
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(SVR_IP);
    server_addr.sin_port = htons(SVR_PORT);
    len = sizeof(struct sockaddr_in);

    /* Create endpoint */
    if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket()");
        return -1;
    } else {
        printf("sock_fd=[%d]\n", sock_fd);
    }

    /* Set socket option */
    if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) < 0) {
        perror("setsockopt()");
        return -1;
    }

    /* Bind */
    if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind()");
        return -1;
    } else {
        printf("bind [%s:%u] success\n",
            inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port));
    }

    /* Listen */
    if (listen(sock_fd, 128) == -1) {
        perror("listen()");
        return -1;
    }

    FD_ZERO(&active_fd_set);
    FD_SET(sock_fd, &active_fd_set);
    max_fd = sock_fd;

    while (1) {
        int             ret;
        struct timeval  tv;
        fd_set          read_fds;

        /* Set timeout */
        tv.tv_sec = 2;
        tv.tv_usec = 0;

        /* Copy fd set */
        read_fds = active_fd_set;
        ret = select(max_fd + 1, &read_fds, NULL, NULL, &tv);
        if (ret == -1) {
            perror("select()");
            return -1;
        } else if (ret == 0) {
            printf("select timeout\n");
            continue;
        } else {
            int i;

            /* Service all sockets */
            for (i = 0; i < FD_SETSIZE; i++) {
                if (FD_ISSET(i, &read_fds)) {
                    if (i == sock_fd) {
                        /* Connection request on original socket. */
                        struct sockaddr_in  client_addr;
                        int                 new_fd;

                        /* Accept */
                        new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &len);
                        if (new_fd == -1) {
                            perror("accept()");
                            return -1;
                        } else {
                            printf("Accpet client come from [%s:%u] by fd [%d]\n",
                                inet_ntoa(client_addr.sin_addr),
                                ntohs(client_addr.sin_port), new_fd);

                            /* Add to fd set */
                            FD_SET(new_fd, &active_fd_set);
                            if (new_fd > max_fd)
                                max_fd = new_fd;
                        }
                    } else {
                        /* Data arriving on an already-connected socket */
                        int recv_len;

                        /* Receive */
                        memset(buff, 0, sizeof(buff));
                        recv_len = recv(i, buff, sizeof(buff), 0);
                        if (recv_len == -1) {
                            perror("recv()");
                            return -1;
                        } else if (recv_len == 0) {
                            printf("Client disconnect\n");
                        } else {
                            printf("Receive: len=[%d] msg=[%s]\n", recv_len, buff);

                            /* Send (In fact we should determine when it can be written)*/
                            send(i, buff, recv_len, 0);
                        }

                        /* Clean up */
                        close(i);
                        FD_CLR(i, &active_fd_set);
                    }

                } // end of if
            } //end of for
        } // end of if
    } // end of while
    
    return 0;
}

2011/06/08

Get IPv4 address from sockaddr or socket

(sock_fd is a socket)
sockaddr_in  addr_in;
int          len = sizeof(sockaddr_in);

/* Get sockaddr_in structure information from socket */
getsockname(sock_fd, (sockaddr *)&addr_in, (socklen_t *)&len);

/* Get address from sockaddr_in */
printf("IP=[%s] port=[%d]\n", inet_ntoa(addr_in.sin_addr), addr_in.sin_port);

2011/05/19

UDP server over libevent - sample code

基本的sample code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <event.h>
#include <event2/listener.h>

#define SVR_IP                          "127.0.0.1"
#define SVR_PORT                        10000
#define BUF_SIZE                        1024

void read_cb(int fd, short event, void *arg) {
    char                buf[BUF_SIZE];
    int                 len;
    int                 size = sizeof(struct sockaddr);
    struct sockaddr_in  client_addr;

    memset(buf, 0, sizeof(buf));
    len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &size);

    if (len == -1) {
        perror("recvfrom()");
    } else if (len == 0) {
        printf("Connection Closed\n");
    } else {
        printf("Read: len [%d] - content [%s]\n", len, buf);
        
        /* Echo */
        sendto(fd, buf, len, 0, (struct sockaddr *)&client_addr, size);
    }
}

int bind_socket(struct event *ev) {
    int                 sock_fd;
    int                 flag = 1;
    struct sockaddr_in  sin;

    /* Create endpoint */
    if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket()");
        return -1;
    }

    /* Set socket option */
    if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) < 0) {
        perror("setsockopt()");
        return 1;
    }

    /* Set IP, port */
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr(SVR_IP);
    sin.sin_port = htons(SVR_PORT);

    /* Bind */
    if (bind(sock_fd, (struct sockaddr *)&sin, sizeof(struct sockaddr)) < 0) {
        perror("bind()");
        return -1;
    } else {
        printf("bind() success - [%s] [%u]\n", SVR_IP, SVR_PORT);
    }

    /* Init one event and add to active events */
    event_set(ev, sock_fd, EV_READ | EV_PERSIST, &read_cb, NULL);
    if (event_add(ev, NULL) == -1) {
        printf("event_add() failed\n");
    }

    return 0;
}

int main(void) {
    struct event  ev;

    /* Init. event */
    if (event_init() == NULL) {
        printf("event_init() failed\n");
        return -1;
    }

    /* Bind socket */
    if (bind_socket(&ev) != 0) {
        printf("bind_socket() failed\n");
        return -1;
    }

    /* Enter event loop */
    event_dispatch();

    return 0;
}