编程接口指南

连接流套接字

以下两个示例说明启动和接受 Internet 系列流连接。

图 7–1 使用流套接字的面向连接的通信

此图形使用接受/连接和读/写功能对说明客户机与服务器之间的数据流。

以下是针对服务器的示例程序。服务器创建套接字并将名称绑定到此套接字,然后显示端口号。此程序调用 listen(3SOCKET) 将套接字标记为可以接受连接请求并初始化请求队列。此程序的其余部分为一个死循环。每次循环都接受一个新的连接并将其从队列中删除,从而创建一个新的套接字。服务器读取并显示此套接字中的消息,然后关闭此套接字。地址绑定中介绍了 in6addr_any 用法。


示例 7–1 接受 Internet 流连接(服务器)

#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#define TRUE 1   
/*
 * This program creates a socket and then begins an infinite loop.
 * Each time through the loop it accepts a connection and prints
 * data from it. When the connection breaks, or the client closes
 * the connection, the program accepts a new connection.
*/
main() {
    int sock, length;
    struct sockaddr_in6 server;
    int msgsock;
    char buf[1024];
    int rval;
    /* Create socket. */
    sock = socket(AF_INET6, SOCK_STREAM, 0);
    if (sock == -1) {
      perror("opening stream socket");
      exit(1);
   }
   /* Bind socket using wildcards.*/
   bzero (&server, sizeof(server));
   server.sin6_family = AF_INET6;
   server.sin6_addr = in6addr_any;
   server.sin6_port = 0;
   if (bind(sock, (struct sockaddr *) &server, sizeof server)
         == -1) {
      perror("binding stream socket");
      exit(1);
   }
   /* Find out assigned port number and print it out. */
   length = sizeof server;
   if (getsockname(sock,(struct sockaddr *) &server, &length)
         == -1) {
      perror("getting socket name");
      exit(1);
   }
   printf("Socket port #%d\n", ntohs(server.sin6_port));
   /* Start accepting connections. */
   listen(sock, 5);
   do {
      msgsock = accept(sock,(struct sockaddr *) 0,(int *) 0);
      if (msgsock == -1)
         perror("accept");
      else do {
         memset(buf, 0, sizeof buf);
         if ((rval = read(msgsock,buf, sizeof(buf))) == -1)
            perror("reading stream message");
         if (rval == 0)
            printf("Ending connection\n");
         else
            /* assumes the data is printable */
            printf("-->%s\n", buf);
      } while (rval > 0);
      close(msgsock);
   } while(TRUE);
   /*
    * Since this program has an infinite loop, the socket "sock" is
    * never explicitly closed. However, all sockets are closed
    * automatically when a process is killed or terminates normally.
    */
   exit(0);
}

要启动连接,示例 7–2 中的客户机程序会创建一个流套接字,然后调用 connect(3SOCKET),指定用于连接的套接字地址。如果存在目标套接字,并且接受了请求,则连接会完成。现在,程序可以发送数据。数据按顺序传送,并且没有消息边界。此连接会在任何一个套接字关闭时销毁。有关此程序中的数据表示例程(如 ntohl(3SOCKET)ntohs(3SOCKET)htons(3SOCKET) 以及 htonl(3XNET))的更多信息,请参见 byteorder(3SOCKET) 手册页。


示例 7–2 Internet 系列流连接(客户机)

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#define DATA "Half a league, half a league . . ."   
/* 
 * This program creates a socket and initiates a connection with 
 * the socket given in the command line. Some data are sent over the 
 * connection and then the socket is closed, ending the connection.
 * The form of the command line is: streamwrite hostname portnumber 
 * Usage: pgm host port 
 */ 
main(int argc, char *argv[])
{
    int sock, errnum, h_addr_index;
    struct sockaddr_in6 server;
    struct hostent *hp;
    char buf[1024];
    /* Create socket. */
    sock = socket( AF_INET6, SOCK_STREAM, 0);
    if (sock == -1) {
        perror("opening stream socket");
        exit(1);
    }
    /* Connect socket using name specified by command line. */
    bzero (&server, sizeof (server));
    server.sin6_family = AF_INET6;
    hp = getipnodebyname(argv[1], AF_INET6, AI_DEFAULT, &errnum);
/*
 * getipnodebyname returns a structure including the network address
 * of the specified host.
 */
    if (hp == (struct hostent *) 0) {
        fprintf(stderr, "%s: unknown host\n", argv[1]);
        exit(2);
    }
    h_addr_index = 0;
    while (hp->h_addr_list[h_addr_index] != NULL) {
        bcopy(hp->h_addr_list[h_addr_index], &server.sin6_addr,
                    hp->h_length);
        server.sin6_port = htons(atoi(argv[2]));
        if (connect(sock, (struct sockaddr *) &server,
                    sizeof (server)) == -1) {
            if (hp->h_addr_list[++h_addr_index] != NULL) {
                /* Try next address */
                continue;
            }
            perror("connecting stream socket");
            freehostent(hp);
            exit(1);
        }
        break;
    }
    freehostent(hp);
    if (write( sock, DATA, sizeof DATA) == -1)
        perror("writing on stream socket");
    close(sock);
    freehostent (hp);
    exit(0);
}

可以将一对一 SCTP 连接支持添加到流套接字。以下示例代码将 -p 添加到现有程序,使此程序可以指定要使用的协议。


示例 7–3 将 SCTP 支持添加到流套接字

#include <stdio.h>

#include <netdb.h>

#include <string.h>

#include <errno.h>



int

main(int argc, char *argv[])

{

 struct protoent *proto = NULL;

 int c;

 int s;

 int protocol;



 while ((c = getopt(argc, argv, "p:")) != -1) {

  switch (c) {

  case 'p':

   proto = getprotobyname(optarg);

   if (proto == NULL) {

    fprintf(stderr, "Unknown protocol: %s\n",

        optarg);

    return (-1);

   }

   break;

  default:

   fprintf(stderr, "Unknown option: %c\n", c);

   return (-1);

  }

 }



 /* Use the default protocol, which is TCP, if not specified. */

 if (proto == NULL)

  protocol = 0;

 else

  protocol = proto->p_proto;



 /* Create a IPv6 SOCK_STREAM socket of the protocol. */

 if ((s = socket(AF_INET6, SOCK_STREAM, protocol)) == -1) {

  fprintf(stderr, "Cannot create SOCK_STREAM socket of type %s: "

      "%s\n", proto != NULL ? proto->p_name : "tcp",

      strerror(errno));

  return (-1);

 }

 printf("Success\n");

 return (0);

}