socket编程之tcp

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值