socket通信

        网络程序都是有两个部分组成,client端与server端,它们之间建立连接的步骤一般为

  • 服务器端:socket->bind->listen->accept
  • 客户端:socket->connect

        示意图如下:


        Linux系统是通过提供套接字(socket)来进行网络编程的。网络程序通过socket和其它几个函数的调用,会返回一个通讯的文件描述符,我们可以将这个描述符看成普通的文件的描述符来操作,这就是linux的设备无关性的好处。接下来对几个重要函数进行分析:

        (1) int socket(int domain, int type,int protocol)        

        domain:说明我们网络程序所在的主机采用的通讯协议族(AF_UNIX和AF_INET等)。AF_UNIX只能够用于单一的Unix系统进程间通信,而AF_INET是针对Internet的,因而可以允许在远程主机之间通信(当我们man socket时发现domain可选项是PF_*而不是AF_*,因为glibc是posix的实现,所以用PF代替了AF,不过我们都可以使用的)。
        type:我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM等) ,SOCK_STREAM表明我们用的是TCP协议,这样会提供按顺序的、可靠、双向、面向连接的比特流。SOCK_DGRAM表明我们用的是UDP协议,这样只会提供定长的、不可靠、无连接的通信。
protocol:由于我们指定了type,所以这个地方我们一般只要用0来代替就可以了。

      socket函数为网络通讯做基本的准备。成功时返回文件描述符,失败时返回-1,看errno可知道出错的详细情况。

        (2) int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

        sockfd:是由socket函数调用返回得到的文件描述符。

        addr:当一个socket被创建出来后,存在于命名空间之中,但未被赋予实际地址,bind函数的作用就是将地址addr赋值给socket,该操作通俗的说称为为socket命名。sockaddr结构体定义如下:

           struct sockaddr {
               sa_family_t sa_family;
               char        sa_data[14];
           }
但一般编程中并不直接针对此数据结构操作,而是使用另一个与sockaddr等价的数据结构sockaddr_in
struct  sockaddr_in {
short  int  sin_family;                      /* Address family */
unsigned  short  int  sin_port;       /* Port number */
struct  in_addr  sin_addr;              /* Internet address */
unsigned  char  sin_zero[8];         /* Same size as struct sockaddr */
};
struct  in_addr {
unsigned  long  s_addr;
};

typedef struct in_addr {
union {
            struct{
                        unsigned char s_b1,
                        s_b2,
                        s_b3,
                        s_b4;
                        } S_un_b;
           struct {
                        unsigned short s_w1,
                        s_w2;
                        } S_un_w;
            unsigned long S_addr;
          } S_un;
} IN_ADDR
sin_family指代协议族,在socket编程中只能是AF_INET
sin_port存储端口号(使用网络字节顺序)
sin_addr存储IP地址,使用in_addr这个数据结构
sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。

s_addr按照网络字节顺序存储IP地址

sockaddr_in和sockaddr是并列的结构,指向sockaddr_in的结构体的指针也可以指向sockaddr的结构体,并代替它。也就是说,你可以使用sockaddr_in建立你所需要的信息,在最后用进行类型转换就可以了bzero((char*)&mysock,sizeof(mysock));//初始化
mysock结构体名
mysock.sa_family=AF_INET;
mysock.sin_addr.s_addr=inet_addr("192.168.0.1");
……
等到要做转换的时候用:
(struct sockaddr*)mysock

        进程间通信的一种方式是使用UNIX套接字,人们在使用这种方式时往往用的不是网络套接字,而是一种称为本地套接字的方式。这样做可以避免为黑客留下后门。不同于网络套接字的绑定,本地套接字的绑定的是struct sockaddr_un结构。struct sockaddr_un结构有两个参数:sun_family、sun_path。sun_family只能是AF_LOCAL或AF_UNIX,而sun_path是本地文件的路径。通常将文件放在/tmp目录下。

        (3) int listen(int sockfd,int backlog)

        sockfd:是bind后的文件描述符。

        backlog:设置请求排队的最大长度。当有多个客户端程序和服务端相连时,使用这个表示可以容纳的排队长度。listen函数将bind的文件描述符变为监听套接字。返回的情况和bind一样。

        (4) int accept(int sockfd, struct sockaddr *addr,int* addrlen)

        sockfd:listen后的文件描述符

        addr,addrlen:用来给客户端的程序进行填写,服务器只需要传递指针。accept调用时服务器端的程序会一直阻塞到有一个客户程序发出了连接。accept成功时返回最后的服务器端的文件描述符,这个时候服务器端可以向该描述符写信息了。失败时返回-1。

        (5) int connect(int sockfd, struct sockaddr * serv_addr,int addrlen)

        sockfd:socket返回的文件描述符
        serv_addr:储存了服务器端的连接信息 

        addrlen:serv_addr的长度 

        connect函数是客户端用来同服务端连接的。成功时返回0,sockfd是同服务端通讯的文件描述符,失败时返回-1。

        

        示例如下,client端程序为:

        

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

#define EHCO_PORT 8080
#define MAX_COMMAND 10

void *memsets(void *s, int c, int count) {
  char *xs = s;
  while (count--) {
    *xs++ = c;
  }
  return s;
}

#define bzero(b, len) memsets((b), '\0', (len))

int main() {
  int sock_fd;
  struct sockaddr_in serv_addr;
  char *buff[MAX_COMMAND] = {"abc", "def", "test", "hello", "quit"};
  char tmp_buff[100];
  socklen_t len;
  int n,i;

/*create socket*/
  sock_fd = socket(AF_INET, SOCK_STREAM, 0);
  if (sock_fd == -1) {
    printf("create socket error! \r\n");
    return 0;
  } else {
    printf("success to create socket %d \r\n", sock_fd);
  }

/*setting server addr*/
  bzero(&serv_addr, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(EHCO_PORT);
  serv_addr.sin_addr.s_addr = htons(INADDR_ANY);
  bzero(&(serv_addr.sin_zero), 8);

/*connect server*/
  if (connect(sock_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) {
    printf("connect error \r\n");
    close(sock_fd);
    return 0;
  }
  printf("success connect to server \r\n");

/*send and receive data*/
  for (i = 0;i < MAX_COMMAND;i++) {
    send(sock_fd, buff[i], 100, 0);
    n = recv(sock_fd, tmp_buff, 100, 0);
    tmp_buff[n] = '\0';
    printf("data send :%s receive : %s \n", buff[i], tmp_buff);
    if (0 == strncmp(tmp_buff, "quit", 4)) {
      printf("tmp_buff is quit\n");
      break;
    }
  }
  close(sock_fd);
  return 0;
}

        server端程序为

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

#define EHCO_PORT 8080
#define MAX_CLIENT_NUM 10

void *memsets(void *s, int c, int count) {
  char *xs = s;
  while (count--) {
    *xs++ = c;
  }
  return s;
}

#define bzero(b, len) memsets((b), '\0', (len))

int main() {
  int sock_fd;
  struct sockaddr_in serv_addr;
  int client_fd;
  struct sockaddr_in client_addr;
  char buff[101];
  socklen_t len;
  int closing = 0;
  int n;

/*create socket*/
  sock_fd = socket(AF_INET, SOCK_STREAM, 0);
  if (sock_fd == -1) {
    printf("create socket error\n");
    return 0;
  } else {
    printf("success to create socket [%d]\n", sock_fd);
  }

/*setting server addr*/
  bzero(&serv_addr, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(EHCO_PORT);
  serv_addr.sin_addr.s_addr = htons(INADDR_ANY);
  bzero(&(serv_addr.sin_zero), 8);

/*bind addr with socket*/
  if (bind(sock_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) != 0) {
    printf("bind address fail! %d\n", errno);
    close(sock_fd);
    return 0;
  } else {
    printf("success to bind address\n");
  }

/*setting listener*/
  if (listen(sock_fd, MAX_CLIENT_NUM) != 0) {
    printf("listen socket error\r\n");
    close(sock_fd);
    return 0;
  } else {
    printf("success to bind listener \r\n");
  }

/*create another socket*/
    len = sizeof(client_addr);
    client_fd = accept(sock_fd, (struct sockaddr*)&client_addr, &len);
    if (client_fd <= 0) {
      printf("accept error\n");
      close(sock_fd);
      return 0;
    }

/*get data from client user*/
    while ((n = recv(client_fd, buff, 100, 0)) > 0) {
      buff[n] = '\0';
      printf("number of receive bytes = %d data = %s\n", n, buff);

      fflush(stdout);
      send(client_fd, buff, n, 0);
      if (strncmp(buff, "quit", 4) == 0) {
        printf("tmp_buff is quit\n");
        break;
      }
    }

    close(client_fd);
    close(sock_fd);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值