tcp基本套接字编程函数

基本套接字函数

github地址 https://github.com/can130/Socket
书:http://chuquanl.com/?page_id=69

这里写图片描述

1.socket()

#include <sys/socket.h>
int socket (int family, int type, int protocol);
                //Returns: non-negative descriptor if OK, -1 on error

这里写图片描述

这里写图片描述

这里写图片描述

2.connect函数

TCP客户使用connect函数来建立与tcp服务器的连接

#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
                                            //Returns: 0 if OK, -1 on error

sockfd is a socket descriptor returned by the socket function. The second and third arguments are a pointer to a socket address structure and its size.

3.bind函数

bind函数把一个本地协议地址赋予一个套接字。对于网际地址,协议地址是32位的IPv4地址或128位的IPv6地址与16位的TCp和udp端口号的组合

#include <sys/socket.h>
int bind (int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
                                            // Returns: 0 if OK,-1 on error

对于IPv4地址来说,通配地址通常由常值INADDR_ANY来指定,其值一般为0.他告知内核去选择IP地址,使用如下:

struct sockaddr_in    servaddr;
servaddr.sin_addr.s_addr = htonl (INADDR_ANY);        /* wildcard */

对于IPv6

struct sockaddr_in6  serv;
serv.sin6_addr = in6addr_any;     /* wildcard */

4.listen函数

listen函数仅由tcp服务器调用,做两件事情
1. 当socket函数创建一个套接字时,它被假设为一个主动套接字,也就是说,他是一个即将调用connect发起连接的客户套接字,listen函数把一个未连接的套接字转换成一个被动套接字,指示内核应接受向该套接字的连接请求,调用listen后导致套接字从closed状态转换到listen状态。

  1. listen函数的第二个参数规定了内核应该为相应套接字排队的最大连接个数
#include <sys/socket.h>
#int listen (int sockfd, int backlog);
                                      //Returns: 0 if OK, -1 on error

本函数应该在调用socket和bind函数之后,并在调用accept函数之前调用。

理解backlog参数

listen理解backlog参数

5.accept函数

accept由tcp服务器调用,用于从已完成连接队列头返回下一个已完成连接,如果已完成队列为空,那么进程被投入睡眠(假定套接字为默认的阻塞方式)

#include <sys/socket.h>
int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
                   //Returns: non-negative descriptor if OK, -1 on error

参数cliaddr和addrlen用来返回已连接的对端进程(客户)的协议地址。
addrlen是值-结果参数:调用前由他所引用的整数值作为由cliaddr所指的套接字地址结构长度,返回时,该整数值为内核存放在该套接字地址结构内的准确字节数
如果accept成功,返回内核自动生产的一个全新描述符,代表所返回客户的TCP连接。
第一个参数称为监听套接字(由socket创建,随后也用作bind和listen的地一个参数)

example 显示客户端IP地址和端口号的时间获取服务程序

服务端程序

#include <time.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

#include <arpa/inet.h> //htons
#include <strings.h> //bzero
#include<string.h>
#include<stdio.h>
#include <unistd.h> //close

#define MAXLINE     4096    /* max text line length */
#define LISTENQ     1024    /* 2nd argument to listen() */
#define PORT  6000

int main(int argc, char **argv)
{
    int                 listenfd, connfd;
    socklen_t           len;
    struct sockaddr_in  servaddr, cliaddr;
    char                buff[MAXLINE];
    time_t              ticks;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port        = htons(PORT); /* daytime server */

    bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));

    listen(listenfd, LISTENQ);

    for ( ; ; ) {
        len = sizeof(cliaddr);
        connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len);
        printf("connection from %s, port %d\n",
               inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff)),
               ntohs(cliaddr.sin_port));

        ticks = time(NULL);
        snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
        write(connfd, buff, strlen(buff));

        close(connfd);
    }
}

客户端口

#include <time.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

#include <arpa/inet.h> //htons
#include <strings.h> //bzero
#include<string.h>
#include<stdio.h>
#include <unistd.h> //close
#include "apue.h"  //err_

#define MAXLINE     4096    /* max text line length */
#define LISTENQ     1024    /* 2nd argument to listen() */

#define PORT   6000

int main(int argc, char **argv)
{
    int                 sockfd, n, counter = 0;
    char                recvline[MAXLINE + 1];
    struct sockaddr_in  servaddr;

    if (argc != 2)
        err_quit("usage: a.out <IPaddress>");

    if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        err_sys("socket error");

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port   = htons(PORT);  /* daytime server */
    if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
        err_quit("inet_pton error for %s", argv[1]);

    if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
        err_sys("connect error");

    while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
        counter++;
        recvline[n] = 0;    /* null terminate */
        if (fputs(recvline, stdout) == EOF)
            err_sys("fputs error");
    }
    if (n < 0)
        err_sys("read error");

    printf("counter = %d\n", counter);

    return 0;
}

结果
服务端

can@CAN:~/code/unpv13e/intro$ ./daytimetcpsrv1.out    
connection from 127.0.0.1, port 55030     
connection from 192.168.2.108, port 52000

客户端

can@CAN:~/code/unpv13e/intro$ ./daytimetcpcli1.out 127.0.0.1
Sun Apr  9 12:15:43 2017
counter = 1
can@CAN:~/code/unpv13e/intro$ ./daytimetcpcli1.out 192.168.2.108
Sun Apr  9 12:15:50 2017
counter = 1

典型的并发服务器程序轮廓

当建立一个连接时,accept返回,紧接着调用fork,由子进程服务客服,父进程等待另一个连接,新的客户由子进程提供服务,父进程则关闭已连接套接字(fork的时候将资源复制了一份,子父共享述符)

  pid_t pid;
  int
  listenfd,
  connfd;
  listenfd = Socket( ... );
      /* fill in sockaddr_in{} with server's well-known port */
  Bind(listenfd, ... );
  Listen(listenfd, LISTENQ);
  for ( ; ; ) {
      connfd = Accept (listenfd, ... );     /* probably blocks */
      if( (pid = Fork()) == 0) {
          Close(listenfd);
          doit(connfd);
          Close(connfd);
          exit(0);
          /* child closes listening socket */
          /* process the request */
          /* done with this client */
          /* child terminates */
      }
      Close(connfd);
      /* parent closes connected socket */
  }

getsockname函数和getpeername函数

返回某个套接字关联的本地地址(getsockname) 返回与某个套接字关联的外地地址(getpeername)

#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);
int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);
                     //Both return: 0 if OK, -1 on error

两个函数的最后一个参数都是值-结果参数

example 获取套接字的地址族

编写一个函数 返回套接子的地址族

int
sockfd_to_family(int sockfd)
{
    struct sockaddr_storage ss;
    socklen_t   len;

    len = sizeof(ss);
    if (getsockname(sockfd, (struct sockaddr *) &ss, &len) < 0)
        return(-1);
    return(ss.ss_family);
}

shutdown 函数

终止网络连接的通常方法是调用close函数。不过close函数有两个限制,却可以使用shutdown来避免。
1. close把描述符的引用计数减1,仅在该计数变为0时才关闭套接字。而shutdown不管引用计数
2. close终止读和写两个方向的数据传送。既然tcp是全双工的,有时候我们需要告知对端我们已经完成了数据发送,即使对端仍有数据发改给我们

#include <sys/socket.h>
int shutdown(int sockfd, int howto);
                              //Returns: 0 if OK, –1 on error

这里写图片描述

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值