2个简单的tcp_server和tcp_client的程序。
tcp_server.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
//------------------------------------------------------------------------------
// Macro definition
//------------------------------------------------------------------------------
#define BACKLOG 5
//------------------------------------------------------------------------------
// Function implementation
//------------------------------------------------------------------------------
int main(int argc, char **argv)
{
int listening_socket;
// XXX: step 1, create TCP socket
//int socket(int domain, int type, int protocol);
if ((listening_socket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
fprintf(stderr, "socket() failed: %s\n", strerror(errno));
exit(1);
}
fprintf(stdout, "Create a new TCP socket %d\n", listening_socket);
// XXX: step 1.5, reuse address & reuse port
// XXX: step 2, bind
struct sockaddr_in local_address;
memset(&local_address, 0, sizeof(local_address));
local_address.sin_family = PF_INET;
local_address.sin_port = htons(8000);
local_address.sin_addr.s_addr = inet_addr("127.0.0.1");
// int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
if (bind(listening_socket, (struct sockaddr *) &local_address, sizeof(local_address)) < 0)
{
// On error, -1 is returned, and errno is set appropriately.
fprintf(stderr, "bind() failed: %s\n", strerror(errno));
// FIXME: retry some times?
close(listening_socket);
exit(1);
}
// On success, zero is returned.
fprintf(stdout, "bind() successfully.\n");
// XXX: step 3, listen
// int listen(int sockfd, int backlog);
if (listen(listening_socket, BACKLOG) < 0)
{
// On error, -1 is returned, and errno is set appropriately.
fprintf(stderr, "listen() failed: %s\n", strerror(errno));
close(listening_socket);
exit(1);
}
// On success, zero is returned.
fprintf(stdout, "listen() successfully.\n");
int new_accepted_socket;
struct sockaddr_in peer_address;
socklen_t peer_address_length;
char banner[] = "Welcome to AKAE tcp test server.\n";
for (;;)
{
// XXX: step 4, accept new connections
peer_address_length = sizeof(peer_address);
// int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
if ((new_accepted_socket = accept(listening_socket, (struct sockaddr *) &peer_address, &peer_address_length)) < 0)
{
// On error, -1 is returned, and errno is set appropriately.
fprintf(stderr, "accept() failed: %s\n", strerror(errno));
// FIXME: close current listening socket, create a new listening socket to accept
close(listening_socket);
break;
}
// On success, accept() returns a non-negative integer that is a descriptor for the accepted socket.
// char *inet_ntoa(struct in_addr in);
fprintf(stdout, "Accepted a new connection %d from %s:%d.\n", new_accepted_socket, inet_ntoa(peer_address.sin_addr), ntohs(peer_address.sin_port));
// XXX: step 5, r/w on new_accepted_socket
// ssize_t write(int fd, const void *buf, size_t count);
write(new_accepted_socket, banner, strlen(banner));
// XXX: step 6, close new_accepted_socket
close(new_accepted_socket);
}
// XXX: step 7, close listening socket
close(listening_socket);
return 0;
}
tcp_clinet.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
//------------------------------------------------------------------------------
// Macro definition
//------------------------------------------------------------------------------
#define BUFFER_SIZE 1024
//------------------------------------------------------------------------------
// Function implementation
//------------------------------------------------------------------------------
int main(int argc, char **argv)
{
int fd;
// XXX: step 0.1, read command line arguments use getopt(), getopt_long()
// int getopt(int argc, char *const argv[], const char *optstring);
// int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex);
// XXX: You can generate command line parser code use gengetopt
// XXX: http://www.gnu.org/software/gengetopt/gengetopt.html
// XXX: step 1, create a TCP socket
// int socket(int domain, int type, int protocol);
if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
fprintf(stderr, "socket() failed: %s\n", strerror(errno));
exit(1);
}
fprintf(stdout, "Create a new TCP socket %d.\n", fd);
// XXX: step 1.5, domain name resolve
//struct hostent *gethostbyname(const char *name);
// XXX: step 2, connect to remote server
struct sockaddr_in remote_address;
memset(&remote_address, 0, sizeof(remote_address));
remote_address.sin_family = PF_INET;
remote_address.sin_port = htons(80);
//in_addr_t inet_addr(const char *cp);
remote_address.sin_addr.s_addr = inet_addr("192.168.0.1");
// int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
if (connect(fd, (struct sockaddr *) &remote_address, sizeof(remote_address)) < 0)
{
fprintf(stderr, "connect() failed: %s\n", strerror(errno));
close(fd);
exit(1);
}
fprintf(stdout, "Connected to remote server.\n");
// XXX: step 3, send HTTP GET request
char request[] = "GET / HTTP/1.1\n\n";
//ssize_t write(int fd, const void *buf, size_t count);
write(fd, request, strlen(request));
// FIXME: check return value of write()
// XXX: step 4, read response from remote server
char buffer[BUFFER_SIZE];
ssize_t n;
for (;;)
{
memset(buffer, 0, sizeof(buffer));
// ssize_t read(int fd, void *buf, size_t count);
if ((n = read(fd, buffer, sizeof(buffer))) < 0)
{
fprintf(stderr, "read() failed: %s\n", strerror(errno));
close(fd);
break;
}
else if (n == 0)
{
fprintf(stdout, "Connection closed by peer.\n");
break;
}
else
{
fprintf(stdout, "Read %d bytes from server: %s\n", n, buffer);
}
}
// XXX: step 5, close socket
close(fd);
return 0;
}
Makefile:
CC = gcc
CFLAGS = -Wall -g
LDFLAGS =
TARGETS = tcp_client tcp_server
.PHONY: all clean
all: $(TARGETS)
clean:
rm -f *~ *.o a.out $(TARGETS)
使用select的server:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include "net.h"
#include "utils.h"
// Macro definitions
#define LISTEN_BACKLOG 5
#define CONNECTION_SUPPORT_MAX 1024
#define BUFFER_SIZE 1024
// Global variables
// Local variables
// Function prototype
void exchange_data(connection_t * conn);
void sigpipe_handler(int sig);
// Function implementation
int main(int argc, char **argv)
{
int listening_socket;
// FIXME: use getopt() & getopt_long() to parse command line arguments
if (argc < 3)
{
fprintf(stdout, "Usage: %s <ip> <port>\n", argv[0]);
exit(1);
}
// step 1, socket
//int socket(int domain, int type, int protocol);
if ((listening_socket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
fprintf(stderr, "Fatal error: socket() failed: %s\n", strerror(errno));
// FIXME: how to do? retry?
exit(1);
}
#ifdef _DEBUG_
fprintf(stdout, "Debug: Create a new TCP socket %d.\n", listening_socket);
#endif
// step 1.4, getservbyname
// step 1.5, reuse port, reuse address
int value;
value = 1;
// int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len);
if (setsockopt(listening_socket, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) < 0)
{
fprintf(stdout, "Warning: setsockopt() failed: %s\n", strerror(errno));
}
#ifdef _DEBUG_
fprintf(stdout, "Debug: setsockopt(SO_REUSEADDR) successfully.\n");
#endif
#ifdef SO_REUSEPORT
value = 1;
// int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len);
if (setsockopt(listening_socket, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value)) < 0)
{
fprintf(stdout, "Warning: setsockopt() failed: %s\n", strerror(errno));
}
# ifdef _DEBUG_
fprintf(stdout, "Debug: setsockopt(SO_REUSEPORT) successfully.\n");
# endif
#endif
// step 1.6, setsockopt() to set required options(such as LOWAT, KEEPALIVE, etc.)
// step 2, bind
struct sockaddr_in local_address;
memset(&local_address, 0, sizeof(local_address));
local_address.sin_family = PF_INET;
local_address.sin_port = htons(atoi(argv[2]));
local_address.sin_addr.s_addr = inet_addr(argv[1]);
// int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
if (bind(listening_socket, (struct sockaddr *) &local_address, sizeof(local_address)) < 0)
{
fprintf(stderr, "Fatal error: bind() failed: %s\n", strerror(errno));
// FIXME: retry some times.
close(listening_socket);
exit(1);
}
#ifdef _DEBUG_
fprintf(stdout, "Debug: Bound on %s:%s successfully.\n", argv[1], argv[2]);
#endif
// step 3, listen
//int listen(int sockfd, int backlog);
if (listen(listening_socket, LISTEN_BACKLOG) < 0)
{
fprintf(stderr, "Fatal error: listen() failed: %s\n", strerror(errno));
// FIXME: how to do? retry some times?
close(listening_socket);
exit(1);
}
#ifdef _DEBUG_
fprintf(stdout, "Debug: listening on %s:%s to accept incoming connections.\n", argv[1], argv[2]);
#endif
net_init(CONNECTION_SUPPORT_MAX);
// step 3.1, add listening socket to read fd_set to watch readable state
net_register_read(listening_socket);
// step 3.4, signal();
// void (*signal(int sig, void (*func)(int)))(int);
signal(SIGPIPE, sigpipe_handler);
//signal(SIGPIPE, SIG_IGN);
// step 3.5, daemon_init();
fd_set rset, wset, eset;
struct timeval to;
int n;
for (;;)
{
// step 3.8, set read fdset, write fdset, exception fdset
rset = global_rset;
wset = global_wset;
eset = global_eset;
// step 3.9, set correct timeout
to.tv_sec = 1;
to.tv_usec = 0;
again:
// step 4, select()
// int select(int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout);
if ((n = select(global_fd_max + 1, &rset, &wset, &eset, &to)) < 0)
{
// Otherwise, -1 shall be returned, and errno shall be set to indicate the error.
if (errno == EINTR)
{
goto again;
}
fprintf(stderr, "Error: select() failed: %s\n", strerror(errno));
}
else if (n == 0)
{
// XXX: timeout, do something.
fprintf(stdout, "Debug: timeout, do something here.\n");
}
else
{
// Upon successful completion, the pselect() and select() functions shall return the total number of bits set in the bit masks.
int i;
#ifdef _DEBUG_
fprintf(stdout, "Debug: %d sockets are ready for reading/writing/excepting.\n", n);
#endif
// step 4.5, check all interested fds.
for (i = 0; i <= global_fd_max; i++)
{
if (i == listening_socket)
{
// int FD_ISSET(int fd, fd_set *set);
if (FD_ISSET(i, &rset))
{
// step 5, if listening socket is readable, accept a new connection
#ifdef _DEBUG_
fprintf(stdout, "Debug: %d is ready for accepting new connection.\n", i);
#endif
int new_accepted_socket;
struct sockaddr_in peer_address;
socklen_t peer_address_length;
peer_address_length = sizeof(peer_address);
// int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
if ((new_accepted_socket = accept(i, (struct sockaddr *) &peer_address, &peer_address_length)) < 0)
{
fprintf(stderr, "Fatal: accept() failed: %s\n", strerror(errno));
// FIXME: how to do?
if (errno == EBADF)
{
// FIXME: can it exit?
exit(1);
}
}
#ifdef _DEBUG_
// char *inet_ntoa(struct in_addr in);
fprintf(stdout, "Debug: accepted a new connection from %s:%d, fd = %d\n", inet_ntoa(peer_address.sin_addr), ntohs(peer_address.sin_port), new_accepted_socket);
#endif
// XXX: initialize r/w buffer for new connection
if (net_connection_init(new_accepted_socket, BUFFER_SIZE) == NULL)
{
close(new_accepted_socket);
continue;
}
net_register_read(new_accepted_socket);
//net_register_write(new_accepted_socket);
}
continue;
}
if (FD_ISSET(i, &rset))
{
// step 5.1, if normal socket is readable, to read
#ifdef _DEBUG_
fprintf(stdout, "Debug: %d is ready for reading.\n", i);
#endif
// ssize_t read(int fd, void *buf, size_t count);
connection_t *conn;
ssize_t read_num;
conn = global_connection_list[i];
if ((read_num = read(i, conn->rbuf->data + conn->rbuf->payload_length, conn->rbuf->size - conn->rbuf->payload_length)) < 0)
{
fprintf(stderr, "Fatal error: read failed on connection %d: %s\n", i, strerror(errno));
// FIXME: how to do?
}
else if (read_num == 0)
{
// XXX: connection closed by peer.
close(i);
net_unregister_read(i);
net_unregister_write(i);
net_unregister_exception(i);
net_connection_release(conn);
}
else
{
fprintf(stdout, "Debug: read %d bytes on connection %d.\n", read_num, i);
conn->rbuf->payload_length += read_num;
if (conn->rbuf->payload_length >= conn->rbuf->size)
{
// XXX: increase buffer size
char *p;
p = safe_malloc(conn->rbuf->size * 2);
if (p)
{
memcpy(p, conn->rbuf->data, conn->rbuf->payload_length);
safe_free(conn->rbuf->data);
conn->rbuf->data = p;
p = NULL;
}
}
}
// XXX: process incoming data.
exchange_data(conn);
conn->rbuf->payload_length = 0;
}
if (FD_ISSET(i, &wset))
{
// step 5.2, if normal socket is write-able, to write
#ifdef _DEBUG_
fprintf(stdout, "Debug: %d is ready for writting.\n", i);
#endif
connection_t *c;
c = global_connection_list[i];
if (c == NULL)
continue;
ssize_t written;
// ssize_t write(int fd, const void *buf, size_t count);
if ((written = write(i, c->sbuf->data, c->sbuf->payload_length)) < 0)
{
fprintf(stderr, "Fatal error: write() failed: %s\n", strerror(errno));
// FIXME: how to do?
}
else
{
//void *memmove(void *dest, const void *src, size_t n);
memmove(c->sbuf->data, c->sbuf->data + written, c->sbuf->payload_length - written);
c->sbuf->payload_length -= written;
if (c->sbuf->payload_length == 0)
{
net_unregister_write(i);
}
}
}
if (FD_ISSET(i, &eset))
{
// step 5.3, if normal socket is exception, handle
#ifdef _DEBUG_
fprintf(stdout, "Debug: %d is ready for handling exception.\n", i);
#endif
// FIXME: how to do?
}
}
}
}
// step 6, close
close(listening_socket);
// XXX: release all resources
net_release();
return 0;
}
void exchange_data(connection_t * conn)
{
int i;
connection_t *current;
char *p;
for (i = 0; i < global_connection_count_capability; i++)
{
if (i == conn->fd)
continue;
if ((current = global_connection_list[i]))
{
// void *memcpy(void *dest, const void *src, size_t n);
if (current->sbuf->size - current->sbuf->payload_length < conn->rbuf->payload_length)
{
p = safe_malloc(current->sbuf->payload_length + conn->rbuf->payload_length);
if (p)
{
memcpy(p, current->sbuf->data, current->sbuf->payload_length);
safe_free(current->sbuf->data);
current->sbuf->data = p;
p = NULL;
}
else
{
// FIXME: if buffer increase failed, how to do?
}
}
if (current->sbuf->size - current->sbuf->payload_length >= conn->rbuf->payload_length)
{
memcpy(current->sbuf->data + current->sbuf->payload_length, conn->rbuf->data, conn->rbuf->payload_length);
current->sbuf->payload_length += conn->rbuf->payload_length;
}
if (current->sbuf->payload_length > 0)
{
net_register_write(i);
}
}
}
}
void sigpipe_handler(int sig)
{
int errno_save;
errno_save = errno;
fprintf(stdout, "Caught SIGPIPE, continue.\n");
errno = errno_save;
}