Linux 提供的socket API解析

  1. 连接协议

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

domain

指明所使用的的协议族,通常AF_INEF ,表示互联网协议族(TCP/IP协议族);

  • AF_INET IPV4 英特网域

  • AF_INET6 IPV6 英特网域

  • AF_UNIX Unix 域

  • AF_ROUTE 路由套接字

  • AF_KEY 密钥套接字

  • AF_UNSPEC 未指定

type 参数指定socket的类型

  • SOCK_STREAM:

流式套接字提供可靠的、面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性

  • SOCK_DGRAM

数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且保证可靠、无差错的。它使用数据协议UDP

  • SOCK_RAW

允许使用低层协议,原始套接字允许对低层协议如IP或ICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发。

protocol

通常赋值为 0

  • 0选择type类型对应的默认协议

  • IPPROTO_TCP TCP 传输协议

  • IPPROTO_UDP UDP 传输协议

  • IPPROTO_SCTP SCTP 传输协议

  • IPPROTO_TIPC TIPC 传输协议

  1. 地址准备好

bind() 函数: IP号端口号与相应描述字符值函数

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklent addrlen)

功能

  • 用于绑定IP地址和端口号到socketfd

参数

  • sockfd

是一个socket 描述符

  • addr

是一个指向包含有本机IP地址及端口号等信息的sockaddr 类型的指针,指向要绑定给sockf的协议地址结构,这个地址结构根据地址创建socket 时的协议族不同而不同

// IPV4 对应
struct sockaddr 
{
    unisgned short as_family;   // 协议族
    char sa_data[4];  // IP + 端口
}

// 同等替换
struct sockaddr_in
{
    sa_family_t sin_family;    // 协议族
    in_port_t sin_port;        // 端口号
    struct in_addr sin_addr;   // IP地址结构体
    unsigned char sin_zero[8]; // 填充 没有实际意义,只是为跟sockaddr结构体在内存中对齐,这样二者才能相互转换
}

地址转换API

// 把字符串形式的 192.168.1.123 转换为网络能识别的格式
int inet_aton(const char* straddr, struct in_addr *addrp);

// 把网络格式的IP地址转为字符串形式
char* inet_ntoa(struct in_addr inaddr);
  1. 监听

#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd, int backlog);
  1. 连接

#include <sys/types.h>
#include <sys/socket.h>

int accept(
    int sockfd, 
    struct sockaddr *addr, 
    socklen_t *addrlen
);

数据收发

字节流读取函数

在套接字通信中进行字节读取函数:read(),write()。与I/O中读取函数略有区别,因为它们输入或输出的字节数比可能请求的少
ssize_t write(int fd, const void* buf, size_t nbytes);
ssize_t read(int fd, void *buf, size_t nbyte);

// 函数均返回读或写的字节个数,出错返回-1

数据收发常用第二套API

  • 在TCP套接字上发送数据函数:有连接

ssize_t send(int s, const void *msg, size_t len, int flags);
// 包含3个要素:套接字s,  待发数据msg,  数据长度len
// 函数只能对处于连接状态的套接字使用,参数s为已建立好连接的套接字描述
// 符,即accept函数的返回值
// 参数msg 指向存放待发送数据的缓冲区
// 参数len为待发送数据的长度,参数flags为控制选项,一般设置为0
  • 在TCP套接字上收数据函数:有连接

ssize_t recv(int s, void *buf, size_t len, int flags);

// 包含3要素: 套接字s,接收缓冲区buf,长度len
// 函数recv从参数s所指定的套接字描述符(必须面向连接的套接字)上接收
// 数据并保存到参数buf所指定的缓存区
// 参数len则为缓冲区长度,参数flags为控制选项,一般设置为0

客户端的connect函数

  • connect()函数: 客户端连接主机

#include <sys/types.h>
#include <sys/socket.h>

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

功能

  • 该函数用于绑定之后的client端(客户端),与服务器建立连接

参数

  • sockfd

目的服务器的 sockect 描述符

  • addr

服务器端的IP地址和端口号的地址结构指针

  • addrlen

地址长度常被设置为 sizeof(struct sockaddr)

返回值

  • 成功返回 0,遇到错误时返回 -1, 并且errno中包含相应的错误码。

字节序转换API

#include <netinet/in.h>

uint16_t htons(uint16_t host16bitvalue);    //返回网络字节序的值
uint32_t htonl(uint32_t host32bitvalue);    //返回网络字节序的值
uint16_t ntohs(uint16_t net16bitvalue);     //返回主机字节序的值
uint32_t ntohl(uint32_t net32bitvalue);     //返回主机字节序的值


h代表host,n代表net,s代表short(两个字节),l代表long(4个字节),
通过上面的4个函数可以实现主机字节序和网络字节序之间的转换。
有时可以用INADDR_ANY,INADDR_ANY指定地址让操作系统自己获取

大致流程

实现聊天室的功能

server

#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
    int s_fd;
        int n_read;
        char readBuf[128];
        int c_fd;

        int mark = 0;
        char msg[128] = {0};
        //char *msg = "I get your connect";

        struct sockaddr_in s_addr;
        struct sockaddr_in c_addr;

        if(argc != 3){
                printf("param is not good\n");
                exit(-1);
        }

        memset(&s_addr, 0, sizeof(struct sockaddr_in));
        memset(&c_addr, 0, sizeof(struct sockaddr_in));

        // 1. socket
        s_fd = socket(AF_INET, SOCK_STREAM, 0);
        if(s_fd == -1){
                perror("socket");
                exit(-1);
        }
        memset(&s_addr, 0, sizeof(struct sockaddr_in));
        memset(&c_addr, 0, sizeof(struct sockaddr_in));

        // 1. socket
        s_fd = socket(AF_INET, SOCK_STREAM, 0);
        if(s_fd == -1){
                perror("socket");
                exit(-1);
        }
        s_addr.sin_family = AF_INET;
        s_addr.sin_port = htons(atoi(argv[2]));
        inet_aton(argv[1], &s_addr.sin_addr);

        // 2. bind
        bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));

        // 3. listen
        listen(s_fd, 10);

        // 4. accept
        int clen = sizeof(struct sockaddr_in);
         while(1){
                c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen);
                if(c_fd == -1){
                        perror("accept");
                }
                mark++;
                printf("get connect : %s\n", inet_ntoa(c_addr.sin_addr));
                if(fork() == 0){
                        if(fork() == 0){
                                while(1){
                                        sprintf(msg, "welcom No, %d client", mark);
                                        write(c_fd, msg, strlen(msg));
                                        sleep(3);
                                }
                        }

                        // 5. read
                        while(1){
                                memset(readBuf, 0, sizeof(readBuf));
                                n_read = read(c_fd, readBuf, 128);
                                if(n_read == -1){
                                        perror("read");
                                }else{
                                        printf("get message:%d, %s\n",n_read, readBuf);
                                }
                        }
                        break;
                }
                // 6. write
                //write(c_fd, msg, strlen(msg));        
        }
        return 0;
}

client

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
        int c_fd;
        int n_read;
        char readBuf[128];
        //char *msg = "msg from client";
        char msg[128] = {0};
        struct sockaddr_in c_addr;

        memset(&c_addr, 0, sizeof(struct sockaddr_in));
        if(argc != 3){
                printf("param is not good\n");
                exit(-1);
        }

        // 1. socket
        c_fd = socket(AF_INET, SOCK_STREAM, 0);
        if(c_fd == -1){
                perror("socket");
                exit(-1);
        }
        c_addr.sin_family = AF_INET;
        c_addr.sin_port = htons(atoi(argv[2]));
        inet_aton(argv[1], &c_addr.sin_addr);

        // 2.connect
        int con = connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr));
        if(con == -1){
                perror("connect");
                exit(-1);
        }
        while(1){
                if(fork() == 0){
                        while(1){
                                memset(msg, 0, sizeof(msg));
                                printf("input: ");
                                gets(msg);
                                write(c_fd, msg, strlen(msg));
                        }
                }
                while(1){
                        memset(readBuf, 0, sizeof(readBuf));
                        n_read = read(c_fd, readBuf, 128);
                        if(n_read == -1){
                                perror("read");
                        }else{
                                printf("get message:%d, %s\n",n_read, readBuf);
                        }
                }
        }

        // 3.send

        // 4.read       
        return 0;

}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值