socket

网络API(应用程序编程接口),实现相同或不同主机上的进程通信

头文件:
    #include <sys/types.h>   
    #include <sys/socket.h>
原型:
    int socket(int domain, int type, int protocol);
参数:
    int domain:Linux支持的协议族(地址族);
        AF_UNIX, AF_LOCAL   Local communication              unix(7)
        AF_INET             IPv4 Internet protocols          ip(7)
        AF_INET6            IPv6 Internet protocols          ipv6(7);
    int type:套接字类型
        SOCK_STREAM:字节流式套接字----> TCP
        SOCK_DGRAM: 数据报式套接字----> UDP
        SOCK_RAW:原始套接字,传输协议需要在第三个参数指定;
    int protocol:传输协议。如果不需要填0;
    IPPROTO_TCP 和 IPPROTO_UDP;
返回值:
    成功,返回套接字的文件描述符;
    失败,返回-1,更新errno;  
例程:
    //TCP
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sfd < 0)
    {
        perror("socket");
        return -1;
    }

bind

服务器:(必须)绑定本地的IP和端口号到套接字

客户端:(非必需)绑定本地的IP和端口号到套接字

头文件:
    #include <sys/types.h>   
    #include <sys/socket.h>
原型:
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
    int sockfd:socket创建的套接字;
    struct sockaddr *addr:通用地址结构体,一般不采用该结构体,不同的协议族采用不同的地址结构体(详见socket),调用时强制转换到通用地址结构体;
    socklen_t addrlen:地址结构体的大小,sizeof(struct sockaddr_in);
返回值:
    成功,返回0;
    失败,返回-1,更新errno;
例程:
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(PORT);
    saddr.sin_addr.s_addr = inet_addr(IP);
    if(bind(sfd,(struct sockaddr*)&saddr,sizeof(saddr)) < 0)
    {
        ERR_LOG("bind");
        return -1;
    }

listen

将套接字设置为被动监听状态

头文件:
    #include <sys/types.h>  
    #include <sys/socket.h>
原型:
    int listen(int sockfd, int backlog);
参数:
    int sockfd:socket创建的套接字;
    int backlog:允许同时建立多少链接;
        内核会为每一个sockfd创建维护两个队列;
        未完成连接的队列
        完成连接的队列,等着accept函数拿走。
返回值:
    成功,返回0;
    失败,返回-1; 
例程:
    if(listen(sfd,5) < 0)
    {
        ERR_LOG("listen");
        return -1;
    }
    printf("开始监听...\n");

accept

阻塞函数,接收客户端的连接,并返回套接字;

从已完成连接的队列头中,取出一个sfd连接,生成新的套接字newfd

头文件:
    #include <sys/types.h>   
    #include <sys/socket.h>
原型:
    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
    int sockfd:已经被listen设置为被动监听状态的套接字;
    struct sockaddr *addr:存储连接成功后客户端的信息:IP和端口号
    socklen_t *addrlen:第二个结构体的大小,注意是指针类型;
返回值:
    成功,返回一个新的文件描述符,是与客户端建立连接的文件描述符。往这个文件描述符中读写信息,就是与客户端通信;
    失败,返回-1,更新errno; 
例程:
    struct sockaddr_in caddr;
    socklen_t caddrlen = sizeof(caddr);
    int cfd = accept(sfd,(struct sockaddr*)&caddr,&caddrlen);
    if(cfd < 0)
    {
        ERR_LOG("accept");
        return -1;
    }
    printf("连接成功\n");

connnect

根据服务器的IP和端口号连接到服务器

头文件:
    #include <sys/types.h>   
    #include <sys/socket.h>
原型:
    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
    int sockfd:socket创建的套接字;
    struct sockaddr *addr:通用地址结构体,填充服务器的地址信息:IP+端口号;
    socklen_t addrlen:地址结构体的大小;
返回值:
    成功,返回0;
    失败,返回-1,更新errno;
例程:
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(PORT);
    saddr.sin_addr.s_addr = inet_addr(IP);
    if(connect(sfd,(struct sockaddr*)&saddr,sizeof(saddr)) < 0)
    {
        ERR_LOG("connect");
        return -1;
    }
    printf("连接成功\n");

recv

从套接字中获取数据

头文件:
    #include <sys/types.h>
    #include <sys/socket.h>
原型:
    size_t recv(int sockfd, void *buf, size_t len, int flags);
参数:
    int sockfd:服务器:accept函数返回的新的套接字;客户端:socket返回的套接字;
    void *buf:存储读取到的数据;
    size_t len:指定要接收的数据大小;
    int flags:接收方式,0阻塞方式接收;
返回值:
    >0;接收到的字符数;
    ==0; 对方关闭;
    -1; 函数调用出错。
例程:
    recv_len = recv(cfd,buf,N,0);
    if(recv_len < 0)
    {
        ERR_LOG("recv");
        return -1;
    }
    else if(0 == recv_len)
    {
        printf("the client is offline\n");
        close(sfd);
        close(cfd);
        exit(1);
    }

send

将数据发送到套接字中

头文件:
    #include <sys/types.h>
    #include <sys/socket.h>
原型:
    ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数:
    int sockfd:服务器:accept函数返回的新的套接字;客户端:socket返回的套接字;
    void *buf:存储要发送的数据;
    size_t len:发送的数据大小;
    int flags:发送方式,0阻塞方式发送;
返回值:
    成功,返回发送成功的字节数;
    失败,返回-1,更新errno;
例程:
    if(send(cfd,buf,strlen(buf),0) < 0)
    {
        ERR_LOG("send");
        break;
    }

TCP模型

TCP服务器

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#define ERR_LOG(msg) do{\
    perror(msg);\
    printf("%d %s %s\n",__LINE__,__func__,__FILE__);\
}while(0)
#define IP "192.168.1.118"
#define PORT 8888
#define N 128
char buf[N] = "";
void *handler_send(void *arg)
{
    pthread_detach(pthread_self());
    //6、send发送数据
    while(1)
    {
        bzero(buf,sizeof(buf));
        fgets(buf,N,stdin);
        buf[strlen(buf)-1] = 0;
        if(send(*(int*)arg,buf,strlen(buf),0) < 0)
        {
            ERR_LOG("send");
            break;
        }
    }
    pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
    //1、创建socket套接字
    int sfd = socket(AF_INET,SOCK_STREAM,0);
    if(sfd < 0)
    {
        ERR_LOG("socket");
        return -1;
    }
    //2、bind服务器的IP & PORT
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(PORT);
    saddr.sin_addr.s_addr = inet_addr(IP);
    if(bind(sfd,(struct sockaddr*)&saddr,sizeof(saddr)) < 0)
    {
        ERR_LOG("bind");
        return -1;
    }
    //3、listen是否有客户端访问
    if(listen(sfd,5) < 0)
    {
        ERR_LOG("listen");
        return -1;
    }
    printf("开始监听...\n");
    //4、accept成功与客户端建立连接,新建文件描述符用于通信
    struct sockaddr_in caddr;
    socklen_t caddrlen = sizeof(caddr);
    int cfd = accept(sfd,(struct sockaddr*)&caddr,&caddrlen);
    if(cfd < 0)
    {
        ERR_LOG("accept");
        return -1;
    }
    printf("连接成功\n");
    //创建发送数据线程
    pthread_t tid;
    if(pthread_create(&tid,NULL,handler_send,(void*)&cfd) != 0)
    {
        ERR_LOG("pthread_creat");
        return -1;
    }
    int res;
    while(1)
    {
        //5、recv接收数据
        bzero(buf,sizeof(buf));
        res = recv(cfd,buf,N,0);
        if(res < 0)
        {
            ERR_LOG("recv");
            return -1;
        }
        else if(0 == res)
        {
            printf("the client is offline\n");
            close(sfd);
            close(cfd);
            exit(1);
        }
        puts(buf);
    }
    //7、close文件描述符
    close(sfd);
    close(cfd);
    return 0;
}

TCP客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#define ERR_LOG(msg) do{\
    perror(msg);\
    printf("%d %s %s\n",__LINE__,__func__,__FILE__);\
}while(0)
//服务端IP & PORT
#define IP "192.168.1.104"
#define PORT 8888
#define N 128
char buf[N] = "";
void *handler_send(void *arg)
{
    pthread_detach(pthread_self());
    //5、send发送数据
    while(1)
    {
        bzero(buf,sizeof(buf));
        fgets(buf,N,stdin);
        buf[strlen(buf)-1] = 0;
        if(send(*(int*)arg,buf,strlen(buf),0) < 0)
        {
            ERR_LOG("send");
            break;
        }
    }
    pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
    //1、创建socket套接字
    int sfd = socket(AF_INET,SOCK_STREAM,0);
    if(sfd < 0)
    {
        ERR_LOG("socket");
        return -1;
    }
    //2、bind客户端的IP & PORT,非必须

    //3、connect连接服务端
    struct sockaddr_in caddr;
    caddr.sin_family = AF_INET;
    caddr.sin_port = htons(PORT);
    caddr.sin_addr.s_addr = inet_addr(IP);
    if(connect(sfd,(struct sockaddr*)&caddr,sizeof(caddr)) < 0)
    {
        ERR_LOG("connect");
        return -1;
    }
    printf("连接成功\n");
    //创建发送数据线程
    pthread_t tid;
    if(pthread_create(&tid,NULL,handler_send,(void*)&sfd) != 0)
    {
        ERR_LOG("pthread_creat");
        return -1;
    }
    int res;
    while(1)
    {
        //4、recv接收数据
        bzero(buf,sizeof(buf));
        res = recv(sfd,buf,N,0);
        if(res < 0)
        {
            ERR_LOG("recv");
            return -1;
        }
        else if(0 == res)
        {
            printf("the server is offline\n");
            close(sfd);
            exit(1);
        }
        puts(buf);
    }
    //7、close文件描述符
    close(sfd);
    return 0;
}
Last modification:2021 年 04 月 19 日 10 : 22 : 32