GNU/Linux应用程序开发学习笔记(三)套接字编程

南笙酒味 提交于 2020-03-30 06:19:54
套接字编程的各级要素:
`主机接口:网络地址ip
`协议:特定的协议(TCP & UDP)
`端口:client或server的进程终点

套接字
简单的说,套接字就是两个应用程序之间通信管道的终点,这个管道可以唯一的标志一条链接,而应用程序则通过套接字来操作这个管道通信。

通信过程
要想使不同主机的进程通信,就必须使用套接字,套接字是用socket()函数创建,如果需要C/S模式,则需要把server的套接字与地址和端口绑定起来,使用bind(),当上述操作完成后,便可使用listen()来监听这个端口,如果有其他程序来connect,那么server将会调用accept()来接受这个申请并为其服务。client是调用connect()来建立与server之间的连接,这时会使用传说中的三次握手来建立一条数据链接。当连接被建立后,server与client便可以通信了,通信可以使用read()/write(),send()/recv(),sendto()/recvfrom()等一些函数来实现,但是不同的函数作用和使用位置是不同的。当数据传送完后,可以调用close()来关闭server与client之间的链接。上述过程就是不同主机之间进程通信的大致过程,当然这只是一个概要,其中的细节还是很多的。

创建和清除套接字
创建套接字的原型如下
int socket(int domain, int type, int protocol);

函数socket返回一个套接字对象,是一个整数类型。
参数一:使用什么协议,
    AF_INET    -- IPv4
    AF_INET6    --    IPv6。
参数二:套接字的类型,
    SOCK_STREAM -- TCP
    SOCK_DGRAM    -- UDP
参数三:指明要使用的特殊协议,通常只有流类型和数据报类型的需要特殊协议,一般设置为0
套接字地址:
为了进行网络通信,使用结构体sockadr_in来命名套接字:

//定义struct sockaddr_in{    int16_t sin_family;    uint16_t sin_port;    struct in_addr sin_addr;    char sin_zero[8];};struct in_addr{    unint32_t s_addr;};//初始化struct sockaddr_in serveraddr;memset(&serveraddr,0,sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");serveraddr.sin_port = htons(SERVER_PORT);

sin_family一般设置为AF_INET,sin_port为套接字的端口号,sin_addr指明主机的地址。
inet_addr()可以将一个字符串类主机地址转化为32为的地址号,这里它不是简单的把四个8位的数字按照对应的位置拼接成一个32位地址,而是按照相反的方向拼接,也就是说192.168.128.65拼接成的32位数字为十进制65 128 168 192所对应的二进制32位结构体。
htonl()与htons()的解释可以看帮助手册:
DESCRIPTION
    The  htonl()  function  converts  the unsigned integer hostlong from host byte order to network byte order.
    The htons() function converts the unsigned short integer hostshort from host byte order to network byte order.
    The ntohl() function converts the unsigned integer netlong from network byte order to host byte order.
    The ntohs() function converts the unsigned short integer netshort from network byte order to host byte order.
    On the i80x86  the  host byte order is Least Significant Byte first, whereas the network byte order, as used on the Internet, is Most Significant Byte first.


函数bind
bind提供对套接字进行本地命名的方法,这个函数可以用于client与server套接字进行命名,一般更多用于服务器:

int bind(int sock, struct sockaddr* addr, int addrLen);err = bind( serversock, (struct sockaddr*)&serveraddr, sizeof(serveraddr));

函数listen
server要想接受client的连接,必须使用listen()来不断的监听一个server的端口:
int listen( int sock, int backlog);
参数sock:表示前面已经创建的服务器套接字
参数backlog:表示等待连接的客户端队列中一共可以有多少个客户端


函数accept
这个函数可以是server来授予客户端提出的连接,在调用accept之前,必须满足下面三个条件:
`已经创建了server套接字 socket()
`已经这个套接字绑定了一个名字 bind()
`已经在监听server端口 listen()

//desciptionint accept( int sock, struct sockaddr* addr, socklen_t* addrlen);//progint len = sizeof(struct sockaddr);ret = accept( serversock, (struct sockaddr*)&serveraddr, (socklen_t*)&len);if(ret >= 0)    printf("succ");else    printf("fail");
返回值:0表示成功,-1表示失败
参数一:server的套接字
参数二:server的套接字地址
参数三:套接字地址的长度地址,注意要转化 (socklen_t*)

函数connect
这个函数被申请连接的一方使用,一般是client:
//desciptionint connect( int sock, struct sockaddr* addr, socklen_t addrlen);//progint ret = connect( clientsock, (struct sockaddr*)&serveraddr, (socklen_t)sizeof(serveraddr));if(ret >= 0)    printf("succ");else    printf("fail")
返回值:0表示成功,-1表示失败
参数一:client套接字
参数二:server的套接字地址
参数三:套接字地址的长度,注意要转化 (socklen_t)


连接套接字输入输出send/recv
原型:

int send( int sock, const void* msg, int len, unsigned int flags);int recv( int sock, void* buf, int len, unsigned int flags);
返回值:如果成功返回字符串长度,否则返回-1
参数:sock是套接字,msg/buf分别代表发送/接受的字符串,flags为标志
flags:具体可从linux man pages中找到。

非连接套接字函数sendto/recvfrom
这两个函数多数用在UDP的无连接环境下,原型如下:
int sendto( int sock, const void* msg, int len, unsigned int flags, const struct sockaddr* to, socklen_t tolen);int recvfrom( int sock, void* buf, int len, unsigned int flags, struct sockaddr* from, socklen_t fromlen);

套接字选项
为获取某个套接字的选项和这只选项的函数原型如下:
int getsockopt( int sock, int level, int optname, void* optval, socklen_t* optlen);int setsockopt( int sock, int level, int optname, const void* optval, socklen_t optlen); 

示例

下面两个程序分别为客户端和服务器端,由server监听端口,client申请连接,连接成功后,server分别利用send和write函数向client发送数据,当数据接受成功后,client向server返回成功标识

server

/* prog server */#include <stdio.h>#include <unistd.h>#include <wait.h>#include <string.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <ctime>#include <arpa/inet.h>using namespace std;#define MAXN_STR 40/* server的端口号 */int SERVER_PORT;/* 随即生成端口号 */int readport(){    srand(unsigned(time(NULL)));    SERVER_PORT = 30000+rand()%10000;    FILE* fout = fopen("port.in","w");    fprintf(fout,"%d\n",SERVER_PORT);    fclose(fout);    return 0;}int main(){    int serverfd,connectionfd;    int lenclient,lenserver;    int cnt = 0;    /* 定义服务器端和客户端的套接字 */    struct sockaddr_in serveraddr;    struct sockaddr_in clientaddr;    char buff[MAXN_STR+1];    /* server每次随机生成端口号,并且写入port.in文件供client读取 */    readport();    /* 创建一个套接字 & 初始化 */    serverfd = socket(AF_INET, SOCK_STREAM, 0);    memset(&serveraddr,0,sizeof(serveraddr));    serveraddr.sin_family = AF_INET;    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);    serveraddr.sin_port = htons(SERVER_PORT);    lenclient = sizeof(clientaddr);    lenserver = sizeof(serveraddr);    /* 对套接字进行本地命名 */    bind(serverfd, (sockaddr*)&serveraddr, sizeof(serveraddr));    printf("Socket has been created!\n");    printf("Server port : %d\n",SERVER_PORT);    /* 对套接字进行监听 */    listen(serverfd,5);    while(true)    {        printf("===== %d =====\n",cnt++);        printf("Now listening...\n");        /* 接受客户端申请的连接 */        connectionfd = accept(serverfd,(struct sockaddr*)&clientaddr,(socklen_t*)&lenclient);        /* 如果client成功连接到server, 则执行 */        if(connectionfd >= 0)        {            printf("Now the link has been connected.\n");            /* 从客户端的套接字中提取出IP地址 和其他信息*/            int clientip = clientaddr.sin_addr.s_addr;            printf("Client ip : %d.%d.%d.%d\n",clientip&255,(clientip>>8)&255,                    (clientip>>16)&255,(clientip>>24)&255);            printf("Client prot : %d\n",ntohs(clientaddr.sin_port));                        /* 使用send向client发送信息 */            sprintf(buff,"THE SEND MSG");            printf("[SEND] Starting sending [send] msg ...\n");            send(connectionfd, (void*)buff, strlen(buff),0);            recv(connectionfd, (void*)buff, MAXN_STR, 0 );            if(strlen(buff) > 0)                printf("[SUCC] Sending succeed.\n");            else                printf("[FAIL] Sending failed.\n");            /* 使用write向client发送消息 */            sprintf(buff,"THE WRITE MSG");            printf("[SEND] starting sending [write] msg ...\n");            write(connectionfd,buff, strlen(buff));            recv(connectionfd, (void*)buff, MAXN_STR, 0 );            if(strlen(buff) > 0)                printf("[SUCC] Sending succeed.\n");            else                printf("[FAIL] Sending failed.\n");            /* 关闭此连接 */            close(connectionfd);            printf("Disconnect the link.\n");            /* 使用sendto向client发送消息(非连接) */            /*            sprintf(buff, "THE SENDTO MSG");            printf("[SEND] Starting sending [sendto] msg ...\n");            */            /*            sendto(serverfd, (void*)buff, strlen(buff), 0,                    (struct sockaddr*)&clientaddr, sizeof(clientaddr));                    */        }        else        {            /*  与client连接失败 */            printf("ERROR: Failed while establish the link!\n");        }    }    close(serverfd);    return 0;}

client

/* prog client */#include <sys/socket.h>#include <arpa/inet.h>#include <sys/stat.h>#include <sys/types.h>#include <stdio.h>#include <string.h>#include <unistd.h>#include <time.h>#include <stdlib.h>#include <wait.h>#define MAXN_STR 360/* server端口号 */int SERVER_PORT;int CLIENT_PORT;/* 读取server端口 并且 随即打开client端口 */int readport(){    srand(unsigned(time(NULL)));    FILE* fout = fopen("port.in","r");    fscanf(fout,"%d",&SERVER_PORT);    fclose(fout);    CLIENT_PORT = 40000 + rand()%10000;    return 0;}int main(){    char buff[MAXN_STR];    char succ[] = "succ";    char fld[] = "fail";    /* client的套接字 */    int clientfd;    /* server的套接字地址 */    struct sockaddr_in serveraddr;    struct sockaddr_in clientaddr;    /* 读取server端口号 */    readport();    /* 创建client的套接字并且初始化server和client的套接字地址 */    clientfd = socket(AF_INET, SOCK_STREAM, 0);    memset(&serveraddr,0,sizeof(serveraddr));    serveraddr.sin_family = AF_INET;    serveraddr.sin_port = htons(SERVER_PORT);    serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");    clientaddr.sin_family = AF_INET;    clientaddr.sin_port = htons(CLIENT_PORT);    clientaddr.sin_addr.s_addr = inet_addr("127.0.0.1");    /* 绑定client套接字 */    bind(clientfd, (struct sockaddr*)&clientaddr, sizeof(clientaddr));    printf("Socket has been created.\n");    printf("Client port : %d\n",CLIENT_PORT);    /* 向server请求服务 */    printf("Now require scv from server ...\n");    int chk = connect(clientfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));        /* 判断是否连接请求成功 */    if(chk < 0)    {        printf("ERROR: Could not connect to the host!\n");        return 0;    }    else    {        /* 连接成功,并且输出server的信息 */        printf("connection established.\n");        int serverip = serveraddr.sin_addr.s_addr;        printf("Server ip : %d.%d.%d.%d\n",serverip&255,(serverip>>8)&255,                (serverip>>16)&255,(serverip>>24)&255);        printf("Server port : %d\n",ntohs(serveraddr.sin_port));    }    /* 使用recv从server接受数据 */    printf("Starting RECV msg ...\n");    int len = recv(clientfd,(void*)buff,MAXN_STR,0);    buff[len] = 0;    if(len > 0)    {        /* 如果client接受数据成功,则向server发送成功信号 */        printf("[RECV] %s\n",buff);        printf("[SUCC] Recviving succeed.\n");        send(clientfd,(void*)succ,strlen(succ),0);    }    else    {        /* 否则,向server发送失败信号 */        printf("[FAIL] Recviving failed.\n");        send(clientfd,(void*)fld,strlen(fld),0);    }    /* 使用read从server读取数据 */    printf("Starting READ msg ...\n");    len = read(clientfd,buff,MAXN_STR);    buff[len] = 0;    if(len > 0)    {        /* 如果client接受数据成功,则向server发送成功信号 */        printf("[RECV] %s\n",buff);        printf("[SUCC] Recviving succeed.\n");        send(clientfd,(void*)succ,strlen(succ),0);    }    else    {        /* 否则,向server发送失败信号 */        printf("[FAIL] Recviving failed.\n");        send(clientfd,(void*)fld,strlen(fld),0);    }    /* 断开与server的连接 */    close(clientfd);    printf("Now the connection has been broken\n");    /* 使用recvfrom从server接受数据(非链接) */    /*    int serverlen = sizeof(serveraddr);    printf("Starting RECVFROM msg ...\n");    len = recvfrom(clientfd, (void*)buff, MAXN_STR, 0,            (struct sockaddr*)&serveraddr, (socklen_t*)&serverlen);    buff[len] = 0;    if(len > 0)    {        // 如果client接受数据成功,则向server发送成功信号         printf("[RECV] %s\n",buff);        printf("[SUCC] Recviving succeed.\n");    }    else    {        // 否则,向server发送失败信号         printf("[FAIL] Recviving failed.\n");    }    */    close(clientfd);    return 0;}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!