socket套接字TCP API

socket套接字TCP API

socket概念

  • socket又称“套接字”,是计算机网络中进程间通信数据通道的一个端点,或称之为句柄。IP地址+端口号就可以唯一确定一个socket。
  • TCP/IP协议族包括传输层(TCP/UDP),网络层(ICMP/IP/IGMP),链路层(ARP/RARP)。应用层通常使用socket地址,即IP地址+端口号来确定通信的对端。而socket正是TCP/IP协议族与应用层之间的接口层,可以说对上层提供了TCP/IP协议族的一种封装,无需关心更底层的实现。
  • 应用上通常使用一些更高层的协议库来编程,socket更多归类于底层驱动编程。不过熟悉socket总归是有好处的。

套接字地址结构

  • IP地址+端口号可以唯一确定一个socket套接字地址,命名为sockaddr_in,位于netinet/in.h头文件中,定义如下。
    struct in_addr{
      in_addr_t s_addr;
    };
    struct sockaddr_in{
      uint8_t sin_len;
      sa_family_t sin_family;
      in_port_t sin_port;
      struct in_addr sin_addr;
      char sin_zero(0);
    };
  • in_addr_t in_port_t位于netinet/in.h
  • in_addr_t一般定义为uint32_tin_port_t一般定义为uint16_t
  • sa_family_t位于sys/socket.h
  • uint8_t等位于sys/types.h
  • sin_addr sin_port即为以网络字节序存储的32位ip地址与端口号
  • 套接字地址有很多种,为了能够统一以指针的形式使用socket API,使用时需要转换为通用套接字地址sockaddr,一般进行强制类型转换即可。

socket基本TCP API

  • 相关函数定义于sys/socket.h

    socket函数

  • socket函数用以创建一个socket。
    int socket(int family, int type, int protocal);
  • family通常设置为AF_INET``AF_INET6,分别表示IPv4/6协议。
  • type通常设置为SOCK_STREAM SOCK_DGRAM SOCK_RAW,分别表示字节流(TCP),数据报(UDP),原始套接字。
  • protocal表示协议族,IPPROTO_TCP'IPPROTO_UDP`通常设置为0也可。
  • 返回值表示非负套接字描述符

connect函数

  • 用于建立连接
    int connect(int sockfd, const struct sockaddr *servaddr, int addrlen);
  • TCP客户端用connect函数与服务器端建立连接
  • 此函数将激发TCP的三次握手连接过程,直到链接建立成功或出错才返回
  • 每个socket只能调用一次connect,出错后必须close当前socket再次重新依次调用socket、connect
  • 函数参数为socket描述符,通用socket地址指针及其结构体大小。

bind函数

  • bind将IP地址和端口绑定到套接字描述符
    int bind(int sockfd, const struct sockadddr *myaddr, int addrlen);
  • 如果sin_addr.s_addr设置为INADDR_ANY,且主机有多个网络接口,则可以在多个网络接口接受用户connect

listen函数

  • listem将一个未调用connect函数的socket转换为一个被动监听套接字
    int listen(int sockfd, int backlog);
  • backlog规定了挂起连接的最大数量

accept函数

  • 内核为任一个监听套接字维护一个正在处于握手连接阶段的未完成连接队列,以及已完成连接队列
  • accept每次接受一个监听套接字描述符,返回一个已连接队列中的已连接套接字描述符
  • 已连接套接字的套接字地址与地址长度存放于cliaddr与addrlen指向的内存中。如果使用两个0来调用,则无法得到客户端已连接套接字表示的地址与端口等信息。
  • 对于每个处理完毕的连接,应该close,否则可能耗尽套接字描述符
    int accept(int sockfd, struct sockaddr *cliaddr, int *addrlen)

close函数

  • close一个TCP套接字默认行为是把socket标记为关闭后返回。但触发了四次挥手过程
    int close(int sockfd);

传送数据

  • 通常需要一个缓冲区,之后使用recv、send函数接收发送
  • read、write在*nix系统上也可以
    int recv(int sockfd, void *buf, size_t len, int flags);
    int send(int sockfd, const void *buf, size_t len, int flags);

TCP通信客户端与服务器端

客户端流程

  • 使用connect连接服务器之后就可以开始传输数据

服务器端流程

  • 需要首先绑定网络接口(bind),之后进入监听状态(listen),最后从队列中取出一个已经连接的套接字,即获得新连接(accept),之后可以开始传输数据

源代码如下:

  • 服务器端
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<stdio.h>

int main(){
    struct sockaddr_in local;
    int s;
    int sl;
    int rc;
    char buf[1000];

    local.sin_family = AF_INET;
    local.sin_port = htons(7500);
    local.sin_addr.s_addr = htonl(INADDR_ANY);
    s = socket(AF_INET, SOCK_STREAM, 0);
    if ( s < 0){
        perror("socket call failed");
        exit(1);
    }
    rc = bind(s, (struct sockaddr *) &local, sizeof(local));
    if ( rc < 0){
        perror("bind call failed");
        exit(1);
    }
    rc = listen(s, 5);
    if ( rc < 0){
        perror("listen call failed");
        exit(1);
    }
    sl = accept(s, NULL, NULL);
    if ( sl < 0){
        perror("accept call failed");
        exit(1);
    }
    rc = recv(sl, buf, 10, 0);
    if ( rc < 0){
        perror("recv call failed");
        exit(1);
    }
    printf("%s\n", buf);
    rc = send(sl, "good", 10, 0);
    if ( rc < 0){
        perror("send call failed");
        exit(1);
    }
    exit(0);
}
  • 客户端
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>

int main(){
    struct sockaddr_in peer;
    int s;
    int rc;
    char buf[100];

    peer.sin_family = AF_INET;
    peer.sin_port = htons(7500);
    peer.sin_addr.s_addr = inet_addr("127.0.0.1");

    s = socket(AF_INET, SOCK_STREAM, 0);
    if ( s < 0 ){
        perror("socket call failed");
        exit(1);
    }
    rc = connect(s, (struct sockaddr *) &peer, sizeof(peer));
    if (rc){
        perror("connect call failed");
        exit(1);
    }
    rc = send(s, "hello", 10, 0);
    if (rc <= 0)
    {
        perror("send call failed");
        exit(1);
    }
    rc = recv(s, buf, 10, 0);
    if (rc <= 0){
        perror("recv call failed");
    }
    else
        printf("%s\n", buf);

    exit(0);
}

 
 

转载请注明作者:Focustc,博客地址为 http://blog.csdn.net/caozhk,原文链接为 点击打开
  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值