`
lobin
  • 浏览: 417361 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

C: Linux C 编程 - 网络编程

 
阅读更多
写道

 

写道
网络协议 第1篇: TCP & UDP
https://www.iteye.com/blog/lobin-2325790

 

网络编程

 

网络协议 

 

协议分析

 

PCAP

linux下编译libpcap库时不支持rpcap。

 

RPCAP is a Remote Packet Capture system.

 

Windows PCAP

 

 

 

服务器编程

服务器编程中,不可避免的要使用到socket来实现客户端和服务器的连接通信,处理来自客户端的请求。这里不去详细将socket相关的东西,有关socket的东西可以参考另一篇文章:

写道
C: Linux C 编程 - socket
https://www.iteye.com/blog/lobin-2351329

 

这里首先要考虑的是服务端怎么接收客户端的socket连接,客户端和服务器建立连接后,客户端向服务器发送请求,服务器怎么处理这些请求。在传统的socket编程模型中,我们通常在一个无限循环中,循环调用accept来接收客户端连接,在接收到一个客户端连接后,然后再来一个无限循环中,循环调用recv或者read来接收客户端的请求。

 

代码如下:

 

  while (1)
  {
    fd_got = accept(fd, (struct sockaddr *) &sockaddr_got, &sockaddr_got_len);
    while (1) 
    {
      nbytes = recv(fd_got, buffer, size, 0);

      ... ...
    }
  }
在这段代码中,当有一个客户端连接建立后,代码进入内部的无限循环接收客户端的请求。当这样的话,就再也无法跳到外部循环中去接受其他客户端的连接请求。所以这段代码只能处理一个客户端的连接和请求,也就是第一个建立连接的客户端,后面的连接请求都无法accept,也就无法处理这些客户端的请求。
那么是否可以在recv返回后跳出到外部循环继续接收其他客户端的连接请求,这样其实也不行,因为这里recv调用是阻塞式的,想象一下这种情况,如果第一个客户端在建立连接后,一直都没有向服务器发送请求,这意味着这段代码会一直阻塞在recv调用上,无法继续执行下去,这样的话也无法跳出到外部循环。
当然我们也可以在recv的第4个参数flags上指定MSG_DONTWAIT,这样recv调用是非阻塞式的。这样的话,即便是第一个客户端一直都没有服务器发送请求,recv调用也不会阻塞。这样在recv调用后就可以跳出到外部循环继续接收其他客户端的连接请求。但是遗憾的是这里accept调用也是阻塞的,如果一直没有其他客户端请求连接,accept调用也会一直阻塞下去。这样也就无法接收已经建立连接的客户端的数据请求。
而且,如果这样的话,内部的那个循环其实就没什么用,代码如下:
  while (1)
  {
    fd_got = accept(fd, (struct sockaddr *) &sockaddr_got, &sockaddr_got_len);
    nbytes = recv(fd_got, buffer, size, 0);
    ... ...
  }

这样的话,recv调用受accept的影响也无法循环接收来自客户端的请求。而且,就算accept返回后,后面的recv,也是接收的是最新连接的客户端的请求,无法处理之前建立连接的客户端请求。上面的代码,第一个客户端连接后,发送的请求还可以立即接收到,改成这样后,第一个客户端发送的请求又无法接收了。

 

上面的问题一个阻塞导致的问题,一个就是accept、recv调用都在同一线程下,导致他们相互影响。如果我们不考虑阻塞导致的问题,那么我们可以考虑让accept、recv调用在不同的线程或进程上,这样就不会相互影响,各自独立处理自己的接收客户端连接请求和自己的客户端请求。

比如主进程专门负责accept处理客户端连接,连接建立后,fork子进程去处理客户端请求。

  while (1)
  {
    fd_got = accept(fd, (struct sockaddr *) &sockaddr_got, &sockaddr_got_len);

    pid = fork();

    if (pid == 0)
    {
      while (1) 
      {
        nbytes = recv(fd_got, buffer, size, 0);
      }
    }
  }

这段代码和上面的区别就调用了一下fork,改动很小,就能解决上面的问题。主进程不断循环处理客户端连接,一旦后连接,fork一个子进程去处理客户端请求。

 

当然,这里也可以使用线程,同样也可以解决上面的问题。

 

如果考虑通过非阻塞方式来解决上面的问题,

我们可以在创建socket后,将这个socket设置为非阻塞。这有两种方式实现,第一种就是在创建socket的时候,第2个参数指定SOCK_NONBLOCK,第二种方式就是通过fcntl设置O_NONBLOCK。这样,在这个socket下的相关网络调用,主要是些IO调用,都可以非阻塞式调用,不会阻塞。
还是上面的例子,设置为非阻塞后,accept、recv调用都不会阻塞。接着上面的逻辑继续,在recv返回后跳出到外部循环继续接收其他客户端的连接请求,由于accept、recv调用都不会阻塞,代码可以很顺利的执行下去,中间不会出现阻塞情况。
这里还有个问题,上面也提到过,也就是就算设置为非阻塞后,accept返回后,后面的recv,也是接收的是最新连接的客户端的请求,无法处理之前建立连接的客户端请求。所以,我们可以维护一个已经建立连接的客户端fd_set,循环中accept调用后,如果有接收到客户端连接,就把这个接收到的客户端连接的fd添加到fd_set中,然后再循环遍历fd_set中的所有客户端连接fd,去接收各个客户端请求。
  while (1)
  {
    fd_got = accept(fd, (struct sockaddr *) &sockaddr_got, &sockaddr_got_len);
    if (fd_got != -1)
    {
      fd_set_add(fd_set, fd_got);
    }
    for (i = 0; i < nfds; i++) 
    {
      nbytes = recv(fd_set[i], buffer, size, 0);
      ... ...
    }
  }

 

 

 

BIO

BIO即阻塞式IO

 

阻塞式(Blocking):

#include<stdio.h>
#include <stdlib.h>  
#include <string.h>

#include<errno.h>

#include<sys/types.h>

#include<sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <unistd.h>

// posix thread
#include <pthread.h>

int handle_connect(int fd) 
{
	struct sockaddr sock_addr;
	int addrlen = sizeof(struct sockaddr);

	int a_fd = accept(fd, &sock_addr, &addrlen);
	if (a_fd == -1) 
	{
		if (errno == EWOULDBLOCK) 
		{
			
		}
		else 
		{
			printf("accept error %d\n", errno);
		}
		return -1;
	}
	else 
	{
		printf("accept new connection from %d\n", a_fd);
		return a_fd;
	}
}

int start_routine_arg(void *arg) 
{
	return *((int*) arg);
}

void handle_accept(int fd) 
{
	handle_connect(fd);
	handle_accept(fd);
}

void* accept_handler(void *arg) 
{
	pthread_t tid = pthread_self();
	int fd = start_routine_arg(arg);
	handle_accept(fd);

	printf("tid %d\n", tid);
	printf("fd %d\n", fd);

	int *result = (int *) malloc(sizeof(int));  
	*result = 9;
	return (void *) result;
}
// $ objdump -D -S server.exe > server.rasm
int main() 
{
	int fd;
	struct sockaddr_in sock_addr;

	pthread_t pt;

	int result = 0;
	int *presult = &result;
	int **ppresult = &presult;

	printf("%d\n", **ppresult);

	fd = socket(AF_INET, SOCK_STREAM, 0);
	if (fd == -1)
	{
		printf("socket error %d\n", errno);
        return -1;
	}

	printf("fd %d\n", fd);
	
    bzero(&sock_addr, sizeof(struct sockaddr_in));
    sock_addr.sin_family = AF_INET;
    sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    sock_addr.sin_port = htons(2121);

    if(bind(fd, (struct sockaddr *) &sock_addr, sizeof(struct sockaddr)) == -1) 
    {
        printf("bind error %d\n", errno);
        return -1;
    }

	if (listen(fd, 50) == -1) 
    {
        printf("listen error %d\n", errno);
        return -1;
    }

	if (pthread_create(&pt, NULL, accept_handler, (void *) &fd))
	{
		return -1;
	}

	if (pthread_join(pt, (void **) ppresult))
	{
		return -1;
	}

	printf("%d\n", **ppresult);

	printf("...\n");
	return 0;
}

 

NIO

NIO即非阻塞式IO

Socket NIO需要将socket设置为非阻塞式socket。

 

非阻塞式(Non-Blocking):

 

可以通过fcntl函数设置O_NONBLOCK使socket工作在非阻塞式(Non-Blocking)模式下。

从Linux 2.6.27后,还可以在创建socket的时候设置SOCK_NONBLOCK使socket工作在非阻塞式(Non-Blocking)模式下,这种方式是Linux-specific的。

 

以下非阻塞式(Non-Blocking)例子是在创建socket的时候设置SOCK_NONBLOCK使socket工作在非阻塞式(Non-Blocking)模式

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <errno.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <unistd.h>

// posix thread
#include <pthread.h>

#define DEFAULT_HOST "localhost"
#define DEFAULT_PORT 1236

typedef struct Args
{
  char *host;
  int port;
  int backlog;
} Args_t;

Args_t* parseArgs(int argc, char** argv, Args_t* args)
{
  // mask param host setting(0x01), and param port setting(0x02)
  unsigned short mask = 0;
  if (argc > 1)
  {
    char** p_argv = argv;
    int i;
	for (i = 1; i < argc; i++)
	{
      char *s_param = *(p_argv + i);
      if (! strcmp("-h", s_param) || ! strcmp("--help", s_param) || ! strcmp("/?", s_param))
      {
__USAGES:
        printf("%s\n", *argv);
        printf("options:\n");
        printf("\t-h --help /?\n");
		printf("\t  usage.\n");

		printf("\t-H --host[=localhost]\n");
		printf("\t  the host to bind, default to localhost. well-form with formats: \n");
		printf("\t  1) -Hhost, for example: -Hlocalhost\n");
		printf("\t  2) --host=host, for example: --host=localhost\n");
		printf("\t  3) --host host, for example: --host localhost\n");
		printf("\t  if any, for example -Hany Or --host=any Or --host any, this represents INADDR_ANY while binding.\n");

        printf("\t--bind-any\n");
		printf("\t  represents INADDR_ANY while binding. this same as -Hany Or --host=any Or --host any\n");

		printf("\t-p --port[=1236]\n");
		printf("\t  the port to listen, default to 1236. well-form with formats: \n");
		printf("\t  1) -pport, for example: -p1236\n");
		printf("\t  2) --port=port, for example: --port=1236\n");
		printf("\t  3) --port port, for example: --port 1236\n");

		printf("\t--backlog[=50]\n");
		printf("\t  represents the maximum length to which the queue of pending connections.\n");
        return NULL;
      }
      else if (! strncmp("-H", s_param, 2))
      {
        char *host;
        if (mask & 0x01)
        {
          printf("invalid param: %s, dump.\n", s_param);
          goto __USAGES;
        }
		if (strlen(s_param) == 2)
		{
          printf("invalid param: %s.\n", s_param);
          goto __USAGES;
		}
        host = s_param + 2;
		args->host = host;
		mask |= 0x01;
      }
      else if (! strncmp("--host", s_param, 6))
      {
        if (mask & 0x01)
        {
          printf("invalid param: %s, dump.\n", s_param);
          goto __USAGES;
        }

        if (! strcmp("--host", s_param))
        {
          if (i + 1 < argc)
          {
            args->host = *(p_argv + (++i));
          }
          else 
          {
		    printf("invalid param: %s, well-form with formats: --host=host Or --host host\n", s_param);
			goto __USAGES;
		  }
        }
        else if(! strncmp("--host=", s_param, 7))
        {
		  char *token = strtok(s_param, "=");
          if (token != NULL)
          {
            token = strtok(NULL, "=");
            if (token != NULL)
			{
              args->host = token;
			}
			else 
            {
			  printf("invalid param: %s\n", s_param);
			  goto __USAGES;
			}
          }
          else 
          {
			printf("invalid param: %s\n", s_param);
			goto __USAGES;
          }
		} 
		else 
        {
		  printf("invalid param: %s\n", s_param);
          goto __USAGES;
        }
		mask |= 0x01;
      }
	  else if (! strcmp("--bind-any", s_param))
	  {
        args->host = "any";
	  }
      else if (! strncmp("-p", s_param, 2))
      {
        int port;
        if (mask & 0x02)
        {
          printf("invalid param: %s, dump.\n", s_param);
          goto __USAGES;
        }
		if (strlen(s_param) == 2)
		{
          printf("invalid param: %s.\n", s_param);
          goto __USAGES;
		}
        port = atoi(s_param + 2);
        if (port != 0)
        {
          args->port = port;
        }
		else 
        {
          printf("invalid param: %s\n", s_param);
          goto __USAGES;
		}
		mask |= 0x02;
      }
      else if (! strncmp("--port", s_param, 6))
      {
        if (mask & 0x02)
        {
          printf("invalid param: %s, dump.\n", s_param);
          goto __USAGES;
        }
        if (! strcmp("--port", s_param))
        {
          if (i + 1 < argc) 
          {
		    s_param = *(p_argv + (++i));
            int port = atoi(s_param);
		    if (port != 0)
		    {
              args->port = port;
		    }
            else 
            {
		      printf("invalid param: %s\n", s_param);
			  goto __USAGES;
		    }
		  }
          else 
          {
		    printf("invalid param: %s, well-form with formats: --port=port Or --port port\n", s_param);
			goto __USAGES;
		  }
        }
        else if (! strncmp("--port=", s_param, 7))
        {
          char *token = strtok(s_param, "=");
          if (token != NULL)
          {
            token = strtok(NULL, "=");
			if (token != NULL)
			{
              int port = atoi(token);
		      if (port != 0)
		      {
                args->port = port;
		      }
              else 
              {
		        printf("invalid param: %s\n", s_param);
			    goto __USAGES;
		      }
			}
			else 
            {
			  printf("invalid param: %s\n", s_param);
			  goto __USAGES;
			}
          }
          else 
          {
			printf("invalid param: %s\n", s_param);
			goto __USAGES;
          }
        }
        else 
        {
		  printf("invalid param: %s\n", s_param);
          goto __USAGES;
		}
        mask |= 0x02;
      }
      else if (! strncmp("--backlog", s_param, 9)) 
      {
        if (mask & 0x04)
        {
          printf("invalid param: %s, dump.\n", s_param);
          goto __USAGES;
        }
        if (! strcmp("--backlog", s_param))
        {
          if (i + 1 < argc) 
          {
		    s_param = *(p_argv + (++i));
            int backlog = atoi(s_param);
		    if (backlog != 0)
		    {
              args->backlog = backlog;
		    }
            else 
            {
		      printf("invalid param: %s\n", s_param);
			  goto __USAGES;
		    }
		  }
          else 
          {
		    printf("invalid param: %s, well-form with formats: --backlog=backlog Or --backlog backlog\n", s_param);
			goto __USAGES;
		  }
        }
        else if (! strncmp("--backlog=", s_param, 10))
        {
          char *token = strtok(s_param, "=");
          if (token != NULL)
          {
            token = strtok(NULL, "=");
			if (token != NULL)
			{
              int backlog = atoi(token);
		      if (backlog != 0)
		      {
                args->backlog = backlog;
		      }
              else 
              {
		        printf("invalid param: %s\n", s_param);
			    goto __USAGES;
		      }
			}
			else 
            {
			  printf("invalid param: %s\n", s_param);
			  goto __USAGES;
			}
          }
          else 
          {
			printf("invalid param: %s\n", s_param);
			goto __USAGES;
          }
        }
        else 
        {
		  printf("invalid param: %s\n", s_param);
          goto __USAGES;
		}
		mask |= 0x04;
	  }
      else 
      {
        printf("invalid param: %s\n", s_param);
        goto __USAGES;
      }
	}
  }
  return args;
}

int handle_connect(int fd) 
{
	struct sockaddr sock_addr;
	int addrlen = sizeof(struct sockaddr);

    
	int a_fd = accept(fd, &sock_addr, &addrlen);
	if (a_fd == -1) 
	{
		if (errno == EWOULDBLOCK) 
		{
			// ignore, in a non-blocking
		}
		else 
		{
			printf("accept error %d\n", errno);
		}
		return -1;
	}
	else 
	{
		printf("accept new connection from %d\n", a_fd);
		return a_fd;
	}
	
}

int start_routine_arg(void *arg) 
{
	return *((int*) arg);
}

void handle_accept(int fd) 
{
	handle_connect(fd);
	//handle_accept(fd);
}

void* accept_handler(void *arg) 
{
	
	pthread_t tid = pthread_self();
	int fd = start_routine_arg(arg);
	printf("tid %d\n", tid);
	printf("fd %d\n", fd);

	
	while (1)
	{
		handle_accept(fd);
	}
	

	int *result = (int *) malloc(sizeof(int));  
	*result = 9;
	return (void *) result;
}
// $ objdump -D -S server.exe > server.rasm

void main(int argc, char** argv) 
{
  int fd;
  struct sockaddr_in sock_addr; // #include <netinet/in.h>

  Args_t args = {DEFAULT_HOST, DEFAULT_PORT, 50};

  pthread_t pt;

  int result = 0;
  int *presult = &result;
  int **ppresult = &presult;

  printf("%d\n", **ppresult);

  if (parseArgs(argc, argv, &args) == NULL)
  {
    return;
  }
  printf("listening on %s:%d, backlog: %d\n", strcmp("any", args.host) == 0 ? "" : args.host, args.port, args.backlog);

  #ifndef SOCK_NONBLOCK
    #error "SOCK_NONBLOCK not support."
  #endif
  fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
  if (fd == -1)
  {
    printf("socket error %d\n", errno);
    return;
  }
  printf("fd %d\n", fd);

  bzero(&sock_addr, sizeof(struct sockaddr_in)); // #include <string.h>
  sock_addr.sin_family = AF_INET;
  if (! strcmp("any", args.host))
  {
    printf("bind any\n");
    sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  }
  else if (! strcmp("localhost", args.host))
  {
    in_addr_t sin_addr = inet_addr("127.0.0.1");
	if (sin_addr == INADDR_NONE)
	{
      fprintf(stderr, "invalid address %s\n", args.host);
	  return;
	}
	sock_addr.sin_addr.s_addr = sin_addr;
  }
  else 
  {
    if (! inet_aton(args.host, &(sock_addr.sin_addr)))
    {
		fprintf(stderr, "invalid address %s\n", args.host);
        return;
    }
  }
  sock_addr.sin_port = htons(args.port);

  if(bind(fd, (struct sockaddr *) &sock_addr, sizeof(struct sockaddr)) == -1) 
  {
    printf("bind error %d\n", errno);
    return;
  }

  if (listen(fd, args.backlog) == -1) 
  {
    printf("listen error %d\n", errno);
    return;
  }

  if (pthread_create(&pt, NULL, accept_handler, (void *) &fd))
  {
    printf("pthread create error %d\n", errno);
    return;
  }

  if (pthread_join(pt, (void **) ppresult))
  {
    printf("pthread join error %d\n", errno);
    return;
  }

  printf("%d\n", **ppresult);
}
以下非阻塞式(Non-Blocking)例子是通过fcntl函数设置O_NONBLOCK使socket工作在非阻塞式(Non-Blocking)模式
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <errno.h>

#include <fcntl.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <unistd.h>

// posix thread
#include <pthread.h>

#define DEFAULT_HOST "localhost"
#define DEFAULT_PORT 1236

typedef struct Args
{
  char *host;
  int port;
  int backlog;
} Args_t;

Args_t* parseArgs(int argc, char** argv, Args_t* args)
{
  if (argc > 1)
  {
    char** p_argv = argv;
	int port_set_flag = 0, allow_port_0 = 0;
    int i;
	for (i = 1; i < argc; i++)
	{
      char *s_param = *(p_argv + i);
      if (! strcmp("-h", s_param) || ! strcmp("--help", s_param) || ! strcmp("/?", s_param))
      {
__USAGES:
        printf("%s\n", *argv);
        printf("options:\n");
        printf("\t-h --help /?\n");
		printf("\t  usage.\n");

		printf("\t-H --host[=localhost]\n");
		printf("\t  the host to bind, default to localhost. well-form with formats: \n");
		printf("\t  1) -Hhost, for example: -Hlocalhost\n");
		printf("\t  2) --host=host, for example: --host=localhost\n");
		printf("\t  3) --host host, for example: --host localhost\n");
		printf("\t  if any, for example -Hany Or --host=any Or --host any, this represents INADDR_ANY while binding.\n");

        printf("\t--bind-any\n");
		printf("\t  represents INADDR_ANY while binding. this same as -Hany Or --host=any Or --host any\n");

		printf("\t-p --port[=1236]\n");
		printf("\t  the port to listen, default to 1236. well-form with formats: \n");
		printf("\t  1) -pport, for example: -p1236\n");
		printf("\t  2) --port=port, for example: --port=1236\n");
		printf("\t  3) --port port, for example: --port 1236\n");
        printf("\t  the port set to 0 is not a satisfied value, a random port would be assigned. by default the port set to 0 is not allowed\n");

		printf("\t--allow-port-zero\n");
		printf("\t  default, the port set to 0 is not allowed, param --allow-port-zero allow the port set to 0\n");

		printf("\t--backlog[=50]\n");
		printf("\t  represents the maximum length to which the queue of pending connections.\n");
        return NULL;
      }
      else if (! strncmp("-H", s_param, 2))
      {
        char *host;
        if (args->host != NULL)
        {
          printf("invalid param: %s, dump.\n", s_param);
          goto __USAGES;
        }
		if (strlen(s_param) == 2)
		{
          printf("invalid param: %s.\n", s_param);
          goto __USAGES;
		}
        host = s_param + 2;
		args->host = host;
      }
      else if (! strncmp("--host", s_param, 6))
      {
        if (args->host != NULL)
        {
          printf("invalid param: %s, dump.\n", s_param);
          goto __USAGES;
        }

        if (! strcmp("--host", s_param))
        {
          if (i + 1 < argc)
          {
            args->host = *(p_argv + (++i));
          }
          else 
          {
		    printf("invalid param: %s, well-form with formats: --host=host Or --host host\n", s_param);
			goto __USAGES;
		  }
        }
        else if(! strncmp("--host=", s_param, 7))
        {
		  char *token = strtok(s_param, "=");
          if (token != NULL)
          {
            token = strtok(NULL, "=");
            if (token != NULL)
			{
              args->host = token;
			}
			else 
            {
			  printf("invalid param: %s\n", s_param);
			  goto __USAGES;
			}
          }
          else 
          {
			printf("invalid param: %s\n", s_param);
			goto __USAGES;
          }
		} 
		else 
        {
		  printf("invalid param: %s\n", s_param);
          goto __USAGES;
        }
      }
	  else if (! strcmp("--bind-any", s_param))
	  {
        if (args->host != NULL)
        {
          printf("invalid param: %s, dump.\n", s_param);
          goto __USAGES;
        }
        args->host = "any";
	  }
      else if (! strncmp("-p", s_param, 2))
      {
        int port;
        if (args->port > 0)
        {
          printf("invalid param: %s, dump.\n", s_param);
          goto __USAGES;
        }
		if (strlen(s_param) == 2)
		{
          printf("invalid param: %s.\n", s_param);
          goto __USAGES;
		}
        port = atoi(s_param + 2);
        if (port != 0)
        {
          args->port = port;
        }
		else 
        {
          printf("invalid param: %s\n", s_param);
          goto __USAGES;
		}
      }
      else if (! strncmp("--port", s_param, 6))
      {
        if (args->port > 0)
        {
          printf("invalid param: %s, dump.\n", s_param);
          goto __USAGES;
        }
        if (! strcmp("--port", s_param))
        {
          if (i + 1 < argc) 
          {
		    s_param = *(p_argv + (++i));
            int port = atoi(s_param);
		    if (port != 0)
		    {
              args->port = port;
		    }
            else 
            {
		      printf("invalid param: %s\n", s_param);
			  goto __USAGES;
		    }
		  }
          else 
          {
		    printf("invalid param: %s, well-form with formats: --port=port Or --port port\n", s_param);
			goto __USAGES;
		  }
        }
        else if (! strncmp("--port=", s_param, 7))
        {
          char *token = strtok(s_param, "=");
          if (token != NULL)
          {
            token = strtok(NULL, "=");
			if (token != NULL)
			{
              int port = atoi(token);
		      if (port != 0)
		      {
                args->port = port;
		      }
              else 
              {
		        printf("invalid param: %s\n", s_param);
			    goto __USAGES;
		      }
			}
			else 
            {
			  printf("invalid param: %s\n", s_param);
			  goto __USAGES;
			}
          }
          else 
          {
			printf("invalid param: %s\n", s_param);
			goto __USAGES;
          }
        }
        else 
        {
		  printf("invalid param: %s\n", s_param);
          goto __USAGES;
		}
      }
      else if (! strcmp("--allow-port-zero", s_param))
      {
        allow_port_0 = 1;
      }
      else if (! strncmp("--backlog", s_param, 9)) 
      {
        if (args->backlog > 0)
        {
          printf("invalid param: %s, dump.\n", s_param);
          goto __USAGES;
        }
        if (! strcmp("--backlog", s_param))
        {
          if (i + 1 < argc) 
          {
		    s_param = *(p_argv + (++i));
            int backlog = atoi(s_param);
		    if (backlog != 0)
		    {
              args->backlog = backlog;
		    }
            else 
            {
		      printf("invalid param: %s\n", s_param);
			  goto __USAGES;
		    }
		  }
          else 
          {
		    printf("invalid param: %s, well-form with formats: --backlog=backlog Or --backlog backlog\n", s_param);
			goto __USAGES;
		  }
        }
        else if (! strncmp("--backlog=", s_param, 10))
        {
          char *token = strtok(s_param, "=");
          if (token != NULL)
          {
            token = strtok(NULL, "=");
			if (token != NULL)
			{
              int backlog = atoi(token);
		      if (backlog != 0)
		      {
                args->backlog = backlog;
		      }
              else 
              {
		        printf("invalid param: %s\n", s_param);
			    goto __USAGES;
		      }
			}
			else 
            {
			  printf("invalid param: %s\n", s_param);
			  goto __USAGES;
			}
          }
          else 
          {
			printf("invalid param: %s\n", s_param);
			goto __USAGES;
          }
        }
        else 
        {
		  printf("invalid param: %s\n", s_param);
          goto __USAGES;
		}
	  }
      else 
      {
        printf("invalid param: %s\n", s_param);
        goto __USAGES;
      }
	}

    if (! allow_port_0 && args->port == 0)
    {
      fprintf(stderr, "port set to 0 is not allowed\n");
      goto __USAGES;
    }
  }

  return args;
}

int handle_connect(int fd) 
{
	struct sockaddr sock_addr;
	int addrlen = sizeof(struct sockaddr);

    
	int a_fd = accept(fd, &sock_addr, &addrlen);
	if (a_fd == -1) 
	{
		if (errno == EWOULDBLOCK) 
		{
			// ignore, in a non-blocking
		}
		else 
		{
			printf("accept error %d\n", errno);
		}
		return -1;
	}
	else 
	{
		printf("accept new connection from %d\n", a_fd);
		return a_fd;
	}
}

int start_routine_arg(void *arg) 
{
	return *((int*) arg);
}

void handle_accept(int fd) 
{
	handle_connect(fd);
	//handle_accept(fd);
}

void* accept_handler(void *arg) 
{
	
	pthread_t tid = pthread_self();
	int fd = start_routine_arg(arg);
	printf("tid %d\n", tid);
	printf("fd %d\n", fd);

	
	while (1)
	{
		handle_accept(fd);
	}
	

	int *result = (int *) malloc(sizeof(int));  
	*result = 9;
	return (void *) result;
}
// $ objdump -D -S server.exe > server.rasm

void main(int argc, char** argv) 
{
  int fd, fd_flags;
  struct sockaddr_in sock_addr; // #include <netinet/in.h>

  Args_t args;
  memset(&args, 0, sizeof(Args_t));

  pthread_t pt;

  int result = 0;
  int *presult = &result;
  int **ppresult = &presult;

  printf("%d\n", **ppresult);

  if (parseArgs(argc, argv, &args) == NULL)
  {
    return;
  }
  if (args.host == NULL)
  {
    args.host = DEFAULT_HOST;
  }
  if (args.port == 0)
  {
    args.port = DEFAULT_PORT;
  }
  if (args.backlog == 0)
  {
    args.backlog = 50;
  }
  printf("listening on %s:%d, backlog: %d\n", strcmp("any", args.host) == 0 ? "" : args.host, args.port, args.backlog);

  fd = socket(AF_INET, SOCK_STREAM, 0);
  if (fd == -1)
  {
    printf("socket error %d\n", errno);
    return;
  }
  printf("fd %d\n", fd);
  fd_flags = fcntl(fd, F_GETFL);
  if (fd_flags == -1) 
  {
	fprintf(stderr, "fcntl error %d\n", errno);
    return;
  }

  fd_flags |= O_NONBLOCK;
  if (fcntl(fd, F_SETFL, fd_flags) == -1)
  {
    fprintf(stderr, "fcntl error %d\n", errno);
    return;
  }

  bzero(&sock_addr, sizeof(struct sockaddr_in)); // #include <string.h>
  sock_addr.sin_family = AF_INET;
  if (! strcmp("any", args.host))
  {
    printf("bind any\n");
    sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  }
  else if (! strcmp("localhost", args.host))
  {
    in_addr_t sin_addr = inet_addr("127.0.0.1");
	if (sin_addr == INADDR_NONE)
	{
      fprintf(stderr, "invalid address %s\n", args.host);
	  return;
	}
	sock_addr.sin_addr.s_addr = sin_addr;
  }
  else 
  {
    if (! inet_aton(args.host, &(sock_addr.sin_addr)))
    {
		fprintf(stderr, "invalid address %s\n", args.host);
        return;
    }
  }
  sock_addr.sin_port = htons(args.port);

  if(bind(fd, (struct sockaddr *) &sock_addr, sizeof(struct sockaddr)) == -1) 
  {
    printf("bind error %d\n", errno);
    return;
  }

  if (listen(fd, args.backlog) == -1) 
  {
    printf("listen error %d\n", errno);
    return;
  }

  if (pthread_create(&pt, NULL, accept_handler, (void *) &fd))
  {
    printf("pthread create error %d\n", errno);
    return;
  }

  if (pthread_join(pt, (void **) ppresult))
  {
    printf("pthread join error %d\n", errno);
    return;
  }

  printf("%d\n", **ppresult);
}

AIO

AIO即异步IO

 

在调用accept系统调用函数时,出现14号errno错误,查看定义如下:

#defineEFAULT 14/* Bad address */

在https://linux.die.net/man/2/accept4上对该错误描述如下:

EFAULT The addr argument is not in a writable part of the user address space.

这个错误很少遇到,无意中把代码写成:

 

int handle_connect(int fd) 
{
	struct sockaddr sock_addr;
	int addrlen = sizeof(struct sockaddr);

    
	int a_fd = accept(fd, &sock_addr, &addrlen);
	if (a_fd == -1) 
	{
		if (errno == EWOULDBLOCK) 
		{
			// ignore, in a non-blocking
		}
		else 
		{
			printf("accept error %d\n", errno);
		}
		return -1;
	}
	else 
	{
		printf("accept new connection from %d\n", a_fd);
		return a_fd;
	}
	
}

void handle_accept(int fd) 
{
	handle_connect(fd);
	handle_accept(fd);
}
handle_accept内部递归调用,导致出现上面那个错误。

 

将accept这段代码注释掉后,

 

int handle_connect(int fd) 
{
	struct sockaddr sock_addr;
	int addrlen = sizeof(struct sockaddr);

    /*
	int a_fd = accept(fd, &sock_addr, &addrlen);
	if (a_fd == -1) 
	{
		if (errno == EWOULDBLOCK) 
		{
			// ignore, in a non-blocking
		}
		else 
		{
			printf("accept error %d\n", errno);
		}
		return -1;
	}
	else 
	{
		printf("accept new connection from %d\n", a_fd);
		return a_fd;
	}
	*/
}
程序启动后进程就直接退出,没报任何错误,唯一确定是已经出错的原因是因为产生了.stackdump文件,但该文件是空的。也无法根据.stackdump文件去定位原因。
 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    Linux下C语言应用编程(作者-杨铸)配套教学ppt

    这组资源,"Linux下C语言应用编程(作者-杨铸)配套教学ppt",提供了对Linux环境下C语言编程的深入理解和实践指导。以下是一些核心知识点的详细说明: 1. **Linux下C语言编程环境**: - `11-Linux下C语言编程环境....

    学会Linux下C语言编程--基础知识

    C语言编程基础知识 在 Linux 操作系统中,C 语言编程是非常重要的编程技术。要进行 C 语言编程,需要了解一些基础知识,包括源程序编译、Makefile 的编写、程序库的链接、程序的调试、头文件和系统求助等。 1. 源...

    Linux下C语言应用编程-作者-杨铸-配套ppt-源代码-教学大纲

    源代码文件“linuxc.tgz”是一个压缩包,其中包含了书中各个主题的实例代码。学生可以参考这些代码来加深理解,同时通过实际操作来锻炼编程技能。源代码涵盖了上述所有PPT讲解的主题,如简单的文件操作示例、多进程...

    linux C语言 网络编程教程及源码

    linux C语言 网络编程教程及源码 一、网络应用层编程 1、Linux网络编程01——网络协议入门 2、Linux网络编程02——无连接和面向连接的区别 3、Linux网络编程03——字节序和地址转换 4、Linux网络编程04——套接字 5...

    Linux下的C语言编程--基础知识篇.doc

    Linux下的C语言编程基础知识篇 本文将详细介绍在Linux下进行C语言编程所需要的基础知识,包括源程序编译、Makefile的编写、程序库的链接、程序的调试、头文件和系统求助等内容。 一、源程序的编译 在Linux下,...

    Linux下C语言编程--文件操作.rar_c 五子棋_c++ 写文件 linux_linux shell_linux c_

    在上述的压缩包文件中,包含了“Linux下C语言编程--文件操作.rtf”和“www.pudn.com.txt”。这些文档可能提供了更详细的代码示例和解释,建议解压后阅读以加深理解和实践。 总之,无论是C语言还是C++,在Linux环境...

    Linux多线程服务端编程:使用muduo+C网络库

    Linux多线程服务端编程:使用muduo+C网络库.pdf Linux多线程服务端编程:使用muduo+C网络库.pdfLinux多线程服务端编程:使用muduo+C网络库.pdfLinux多线程服务端编程:使用muduo+C网络库.pdfLinux多线程服务端编程:...

    linux-c.zip_C语言_Linux C语言_linux_linux-c

    在Linux环境下进行C语言编程是许多开发者和技术爱好者深入操作系统内核、实现系统级程序或高效应用的重要途径。本文将详细探讨“Linux下C语言”这一主题,基于标题和描述提供的信息,我们将涵盖C语言的基本概念、...

    Linux网络编程-文字版.pdf

    3. C语言编程 文档提到基于C语言进行网络编程。C语言由于其高效和接近硬件的特性,成为编写网络程序的首选语言。它为网络编程提供了丰富的库函数,例如标准I/O库、网络库等。 4. 套接字编程 套接字是网络通信的基本...

    Linux下C语言应用编程--随书源代码

    在Linux环境下进行C语言应用编程是一项基础且重要的技能,它涉及到操作系统、程序设计与系统接口等多个领域的知识。这本书的随书源代码提供了丰富的实例,帮助读者深入理解和实践C语言在Linux下的应用。以下是对这些...

    c语言linux系统编程入门

    ### C语言Linux系统编程入门知识点概述 #### 一、Linux下的C语言编程基础 ##### 1. 源程序的编译 - **GCC编译器**: 在Linux环境下,使用GCC(GNU Compiler Collection)作为主要的编译工具。GCC不仅支持C语言,还...

    头歌Linux系统编程之c编程入门

    【头歌Linux系统编程之C编程入门】是一个关于在Linux环境下进行C语言编程学习的教程。这个教程覆盖了从基础的C语言编程到更高级的Linux系统编程技术,旨在帮助初学者逐步掌握在Linux系统中编写C程序的能力。 1. **...

    LINUX编程白皮书 LINUX编程白皮书

    其次,书中的内容可能涉及C语言编程,因为C语言是Linux编程的主要语言。读者将学习如何使用标准C库,以及如何编写与系统接口交互的代码,如系统调用。此外,书中的章节可能还会讲解C++编程,特别是在Linux环境下的...

    linux下的c语言-网络-网络编程面试题

    Linux 下的 C 语言网络编程面试题 本篇文章针对 Linux 下的 C 语言网络编程面试题进行了详细的解释和分析,涵盖了基础部分和网络/网络编程部分。 基础部分: 1. 程序结果分析:在 32 位 Linux 或 Unix 中,程序的...

Global site tag (gtag.js) - Google Analytics