Linux系统网络编程——套接字一对一通信

用socket套接字实现进程间的一对一通信

TCP协议通信

TCP协议网络通信编程步骤
TCP协议通信所使用的函数和步骤操作都备注在程序中:
服务器端程序:server.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#define SERV_PORT  8900
int main(void )
{
    int ret;
    int socket_serv;
    int socket_client;
    struct sockaddr_in  serv_addr;
    struct sockaddr_in  client_addr;
    socklen_t  addrlen;
    char buf[128];
    /*第一步:创建socket
    int socket(int domain, int type, int protocol);
        domain:
          AF_INET:使用ipv4进行传输,用于tcp和udp通信
          AF_UNIX:用于本地进程通信
          AF_PACKET:tcp和udp都没法满足数据传输要求,可以自定义协议通信方式
        type:
          SOCK_STREAM:tcp(流式套接字)提供面向连接、字节流、安全可靠的通信
          SOCK_DGRAM:udp(数据包套接字)提供无面向连接、数据包、不安全可靠的通信
          SOCK_RAW:raw(原始套接字)
        protocol: 0
    返回值:
        >0:返回套接字文件描述符
        -1:创建套接字失败,并设置errno的值
    */
    socket_serv = socket(AF_INET, SOCK_STREAM, 0);
    if(socket_serv < 0){
        perror("serv socket err");
        return -12;
    }
    /*
    第二步:绑定bind:ip + port(指定IP地址和端口号)
    int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
       sockfd:套接字文件描述符,由socket()函数的返回值得到
       addr:ip + port
            一般不使用struct sockaddr类型,而是使用以下结构体:
            struct sockaddr_in {
               sa_family_t  sin_family;直接填AF_INET
               in_port_t  sin_port;端口号,以网络字节序存放 
               struct  in_addr sin_addr;ip地址的结构体里的成员以网络字节序存放IP地址
               }; 
               struct in_addr {
                   uint32_t  s_addr;ip地址,以网络字节序存放
               };
    注:由于结构体类型不一样,所以在使用的时候还是要将我们初始化的struct sockaddr_in类型结构体变量取地址后强制转化为sockaddr *类型的结构体!
    addrlen:struct sockaddr_in结构体类型的字节大小,比如sizeof(addr)
    返回值:
        0:表示绑定成功
        -1:表示绑定失败,并设置errno的值
*/
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);//htons()将计算机字节序转换为网络字节序
   //serv_addr.sin_addr.s_addr = inet_addr( "192.168.3.137");//inet_addr()将字符型数据转换为整型
    ret = bind(socket_serv,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
    if(ret <0){
        perror("bind err");
        return -13;
    }
    /*
    第三步:监听listen
int listen(int sockfd, int backlog);
sockfd: 套接字文件描述符,由socket()函数的返回值得到
backlog:表示允许有2*backlog+1个连接同时打进来,所以一般写5个
    返回值:int类型
       0:表示监听成功成功
       -1:表示监听失败,并设置errno的值
    */
    ret = listen(socket_serv,5);
    if(ret <0){
        perror("listen err");
        return -14;
    }
    /*
    第四步:接听发送过来的连接accept
    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    addr:函数会将对方的地址相关信息回填到该结构体中,同样是struct sockaddr_in类型的结构体,所以同样要取地址后强制转换为sockaddr *类型
    addrlen: 对方地址长度
    struct sockaddr_in {
       sa_family_t  sin_family;//直接填AF_INET
       in_port_t  sin_port;//端口号,以网络字节序存放 
       struct  in_addr sin_addr;//ip地址的结构体里的成员以网络字节序存放IP地址
    }; 
    struct in_addr {
           uint32_t  s_addr;//ip地址,以网络字节序存放
    };
    返回值:int类型
        >0:表示连接成功,返回一个全新的套接字(另一端的套接字),用于后面的通信
        -1:表示连接失败,并设置errno的值
    */
    addrlen = sizeof(client_addr);
    socket_client = accept(socket_serv,(struct sockaddr *)&client_addr,&addrlen);
    if(socket_client <0){
        perror("accept err:");
        return -16;
    }
    while(1){
    /*使用新的socket接收数据
    ssize_t recv(int sockfd, void *buf, size_t len, int flags);
       buf:接收数据存放的空间
       len: buf的大小
       flags: 0
       返回值:int类型
          >0:实际接收到的数据字节大小
          -1:接收失败,并设置errno
          ==0:对方主动或者意外挂掉连接
*/

        memset(buf,0,sizeof(buf));
        ret = recv(socket_client,buf,sizeof(buf),0);
        if(ret <0){
            perror("recv err:");
            return -17;
        }else if(ret == 0){
            printf("peer close it\n");
            close (socket_client);
            return 0;
        }
        printf("serv get data:%s\n",buf);
        /*使用新的套接字发送数据
            ssize_t send(int sockfd, const void *buf, size_t len, int flags);
            len:要发送的数据的字节大小
            返回值 
                >=0:实际发送的字节大小,int类型
                -1:发送数据失败,并设置errno
        */
        strcpy(buf,"your message is got");
        ret = send(socket_client,buf,sizeof(buf),0);
        if(ret <0){
            perror("send err:");
            return -18;
        }
    }
    close(socket_client);
}

客户端程序:client.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#define SERV_PORT  8900
int main(void )
{
    int ret;
    int socket_serv;
    struct sockaddr_in  serv_addr;
    char buf[128];
    /*第一步,创建socket
    */
    socket_serv = socket(AF_INET, SOCK_STREAM, 0);
    if(socket_serv < 0){
        perror("client socket err");
        return -12;
    }
    /*向服务器发起连接请求connect
    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
        addr:要连接的终端的IP地址及其相关信息,写法同bind函数和accept函数
        返回值:
            0:表示连接成功
            -1:表示连接失败,并设置errno
    */
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    serv_addr.sin_addr.s_addr =  inet_addr( "192.168.3.137");
    ret = connect(socket_serv,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
    if(ret <0){
        perror("connect err");
        return -12;
    }
    while(1){
        strcpy(buf,"client info : nihaoma?");
        ret = send(socket_serv,buf,sizeof(buf),0);
        if(ret <0){
            perror("client send err:");
            return -18;
        }
        memset(buf,0,sizeof(buf));
        ret = recv(socket_serv,buf,sizeof(buf),0);
        if(ret <0){
            perror("client recv err:");
            return -17;
        }else if(ret == 0){
            printf("peer close it\n");
            close (socket_serv);
            return 0;
        }
        printf("client get data:%s\n",buf);
        sleep(1);
    }
    close(socket_serv);
    return 0;
}

上述程序中,实现了两个终端间的一对一网络通信,并成功能互相传输单个数据。那么,要实现两个终端间的多数据传输就要将多个数据作为结构体成员,封装到结构体中,在两个终端进行通信时,直接发送和接收已经定义好的结构体即可。在写网络通信程序之前就要先定义一个结构体类型,结构体成员中定义需要传输的数据变量(成员),而且为了让程序中的每一个发送和接收的函数都使用结构体,要将结构体类型定义为全局变量。因此,为方便编程,直接将结构体定义在一个头文件中,在程序中引用这个头文件。
值得注意的是:数据在网络通信过程中,需要区分大端模式和小端模式。int类型、short类型和long类型的数据都要注意字节序的问题,在发送之前要将数据转换为网络字节序,在接收之后要转换为主机字节序。另外,char类型的数据不需要注意字节序的问题,可以不用来回转换,所以一般在网络编程中能使用char类型的数据就使用char类型。还要注意的是,在网络中的数据传输绝对不能有浮点型(float)数据!

TCP协议端对端多数据网络通信程序

头文件com.h

#ifndef TCP_STRUCT_TEST_H
#define TCP_STRUCT_TEST_H
#define PORT 7200
struct data_package
{
    int temp;
    int hum;
    short cnt;
    char info[64];
}__attribute__((packed));
#endif
/*
__attribute__((packed));表示告诉编译器, 不要对该结构进行任何字节对齐操作,保持原有的数据字节序。
一般情况下,在定义结构体后,gcc编译器要对结构体中的数据进行一个字节对齐的操作。
什么是字节对齐?指在分配内存时最小的单位,由编译器决定。总空间=n*字节对齐数,会按顺序分配内存,这样会使数据尽可能少占用内存,但是不能更改数据的顺序。比如是4字节对齐,最小内存为4字节或者4的倍数个字节。结构体中的成员最大内存为10字节,但系统会分配给结构体12个字节。
*/

服务器程序server.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>

#include "com.h"

int main(void)
{
    int socket_serv;
    int res;
    int client;
    struct sockaddr_in sockaddr;
    struct sockaddr_in client_addr;
    int resize;
    socklen_t client_len;
    struct data_package data;

    socket_serv = socket(AF_INET,SOCK_STREAM,0);
    if(socket_serv < 0)
    {
        perror("socket()");
        exit(-1);
    }

    sockaddr.sin_family = AF_INET;
    sockaddr.sin_port = PORT;
    //sockaddr.sin_addr.s_addr = inet_addr("192.168.3.188");
    sockaddr.sin_addr.s_addr = INADDR_ANY;

    res = bind(socket_serv,(struct sockaddr *)&sockaddr,sizeof(sockaddr));
    if(res < 0)
    {
        perror("bind()");
        exit(-2);
    }

    res = listen(socket_serv,5);
    if(res < 0)
    {
        perror("listen()");
        exit(-3);
    }

    client_len=sizeof(client_addr);
    client = accept(socket_serv,(struct sockaddr *)&client_addr,\
            &client_len);
    if(client < 0)
    {
        perror("accept()");
        exit(-4);
    }

    printf("connect port:%d,address:%s\n",ntohs(client_addr.sin_port),\
            inet_ntoa(client_addr.sin_addr));
    while(1)
    {
        //resize = recv(client,buf,BUFSIZE,0);
        resize = recv(client,&data,sizeof(data),0);//取结构体的地址及其字节大小
        /*
        接收客户端发过来的数据时,先要定义一个同将要接收数据一样的数据类型变量,用于存放接收过来的数据。在这里接收的数据是一个结构体,那么就要先定义一个相同的结构体类型的结构体变量。
        */
        if(resize < 0)
        {
            perror("recv()");
            exit(-5);
        }
        else if(resize == 0)
        {
            printf("peer close!\n");
            close(client);
            exit(0);
        }

        data.temp = ntohl(data.temp);
        data.hum = ntohl(data.hum);
        data.cnt = ntohs(data.cnt);

        printf("[%d] temperture:%d,humidty:%d,%s\n",data.cnt,data.temp,data.hum,data.info);

        strcpy(data.info,"get your message!");

        //data.cnt = ntohs(data.cnt);
        data.cnt++;
        data.cnt = htons(data.cnt);

        data.temp = htonl(data.temp);
        data.hum = htonl(data.hum);

        res = send(client,&data,sizeof(data),0);
        if(res < 0)
        {
            perror("send()");
            exit(-6);
        }
    }
    close(client);

    exit(0);
}

客户端程序client.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>

#include "com.h"
#define BUFSIZE 128
int main(void)
{
    int socket_client;
    int res;
    struct sockaddr_in sockaddr;
    int resize;
    socklen_t client_len;
    struct data_package data_c;

    socket_client = socket(AF_INET,SOCK_STREAM,0);
    if(socket_client < 0)
    {
        perror("socket()");
        exit(-1);
    }

    sockaddr.sin_family = AF_INET;
    sockaddr.sin_port = PORT;
    sockaddr.sin_addr.s_addr = inet_addr("149.28.27.109");

    res = connect(socket_client,(struct sockaddr *)&sockaddr,sizeof(sockaddr));
    if(res < 0)
    {
        perror("bind()");
        exit(-2);
    }
    data_c.temp = 28;
    data_c.hum = 59;
    data_c.cnt = 1;
    strcpy(data_c.info,"message have sended!");
    while(1)
    {
        data_c.temp = htonl(data_c.temp);
        data_c.hum = htonl(data_c.hum);
        data_c.cnt = htons(data_c.cnt);
        res = send(socket_client,&data_c,sizeof(data_c),0);
        if(res < 0)
        {
            perror("send()");
            exit(-6);
        }

        memset(&data_c,0,sizeof(data_c));
        resize = recv(socket_client,&data_c,sizeof(data_c),0);
        if(resize < 0)
        {
            perror("recv()");
            exit(-5);
        }

        data_c.temp = ntohl(data_c.temp);
        data_c.hum = ntohl(data_c.hum);
        data_c.cnt = ntohs(data_c.cnt);

        printf("[%d] temperture:%d,humidty:%d,%s\n",data_c.cnt,data_c.temp,\
                data_c.hum,data_c.info);
        sleep(1);

    }
    close(socket_client);

    exit(0);
}

UDP协议的socket通信编程

UDP协议网络通信编程步骤
不多说,与前面的程序有点类似,直接上程序。
基于UDP协议的端对端(一对一)通信程序如下:
头文件com,h

#ifndef UDP_STRUCT_TEST_H
#define UDP_STRUCT_TEST_H

#define PORT 5549

struct mesdata
{
    int temp;
    int hum;
    short cnt;
    char des[64];
}__attribute__((packed));

#endif

服务器程序server.h

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <string.h>

#include "com.h"

int main(void)
{
    int sock_serv;
    struct sockaddr_in servaddr;
    int res;
    struct mesdata data;
    struct sockaddr_in cliaddr;
    int recv;
    socklen_t clilen;

    sock_serv = socket(AF_INET,SOCK_DGRAM,0);
    if(sock_serv < 0)
    {
        perror("socket()");
        exit(-1);
    }
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = PORT;
    servaddr.sin_addr.s_addr = INADDR_ANY;
    res = bind(sock_serv,(struct sockaddr *)&servaddr,sizeof(servaddr));
    if(res < 0)
    {
        perror("bind()");
        exit(-2);
    }

    while(1)
    {
        clilen = sizeof(cliaddr);
        memset(&data,0,sizeof(data));
        recv = recvfrom(sock_serv,&data,sizeof(data),0,\
                (struct sockaddr *)&cliaddr,&clilen);
        if(recv < 0)
        {
            perror("recvfrom()");
            exit(-3);
        }
        else if(recv == 0)
        {
            printf("closeed!\n");
            close(sock_serv);
            exit(0);
        }

        data.cnt = ntohs(data.cnt);
        data.cnt++;
        data.cnt = htons(data.cnt);

        res = sendto(sock_serv,&data,recv,0,(struct sockaddr *)&cliaddr,\
                sizeof(cliaddr));
        if(res < 0)
        {
            perror("sendto()");
            exit(-5);
        }
    }
    close(sock_serv);

    exit(0);
}

客户端程序client.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <string.h>

#include "com.h"

int main(void)
{
    int sock_cli;
    struct sockaddr_in cliaddr;
    int res;
    int recv;
    struct mesdata clidata;
    socklen_t clilen;

    sock_cli = socket(AF_INET,SOCK_DGRAM,0);
    if(sock_cli < 0)
    {
        perror("socket()");
        exit(-1);
    }
    cliaddr.sin_family = AF_INET;
    cliaddr.sin_port = PORT;
    cliaddr.sin_addr.s_addr = inet_addr("192.168.3.188");

    clidata.temp = 28;
    clidata.hum = 64;
    clidata.cnt = 0;
    strcpy(clidata.des,"the imformation is");

    while(1)
    {
        clidata.temp = htonl(clidata.temp);
        clidata.hum = htonl(clidata.hum);
        clidata.cnt = htons(clidata.cnt);

        res = sendto(sock_cli,&clidata,sizeof(clidata),0,\
                (struct sockaddr *)&cliaddr,sizeof(cliaddr));
        if(res < 0)
        {
            perror("sendto()");
            exit(-5);
        }

        clilen = sizeof(cliaddr);
        memset(&clidata,0,sizeof(clidata));
        recv = recvfrom(sock_cli,&clidata,sizeof(clidata),0,\
                (struct sockaddr *)&cliaddr,&clilen);
        if(recv < 0)
        {
            perror("recvfrom()");
            exit(-3);
        }
        else if(recv == 0)
        {
            printf("closeed!\n");
            exit(0);
        }
        clidata.temp = ntohl(clidata.temp);
        clidata.hum = ntohl(clidata.hum);
        clidata.cnt = ntohs(clidata.cnt);

        printf("[%d] %s temperture=%d,hum=%d\n",clidata.cnt,clidata.des,\
                clidata.temp,clidata.hum);
        sleep(1);

    }
    close(sock_cli);

    exit(0);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值