网络编程——TCP

简介

使用TCP协议,实现开发板与电脑主机之间的网络通信。

一、TCP概念

传输控制协议 TCP(Transmission Control Protocol):面向连接的,数据传输的单位是报文段,能够提供可靠的交付,是传输层(负责向两个主机进程之间的通信提供服务)的协议之一。

TCP 向它的应用程序提供了面向连接的服务。这种服务有 2 个特点:可靠传输、流量控制(即发送方/接收方速率匹配)。它包括了应用层报文划分为短报文,并提供拥塞控制机制。

二、TCP网络通信交互流程

服务器端

客户端

(1)socket:创建一个套接字(套接字:对网络中不同主机上的应用进程之间进行双向通信的端点的抽象,一个套接字就是网络上进程通信的一端)

(2)bind:对套接字进行地址和端口的绑定。

(1)socket:创建一个套接字

(3)listen:监听、等待客户端连接

(2)connect:连接服务器

(4)accept:获得连接请求,并且建立连接

(5)send/receive:发送或接收数据

(3)send/receive:发送或接收数据

(6)close:关闭连接

(4)close:关闭连接

三、网络编程主要函数

1、socket函数

创建一个套接字。成功返回 文件描述符,失败返回-1

int socket(int domain, int type,int protocol);
  • domain是网络程序所在的主机采用的通讯协族(AF_UNIX 和 AF_INET 等),一般使用AF_INET,因为AF_INET是针对Internet的,允许远程通信。

  • type 是网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM 等)。SOCK_STREAM 表明用的是 TCP 协议; SOCK_DGRAM 表明用的是 UDP 协议。这里使用SOCK_STREAM

  • protocol由于指定了 type,所以这里一般只要用 0 来代替

2、bind函数

将地址和端口绑定到一个套接字。成功返回 0,失败返回-1

int bind(int sockfd, struct sockaddr_in *my_addr, int addrlen);

//sockaddr_in 的定义:
struct sockaddr_in{
unsigned short sin_family;      //如果使用Internet,一般为 AF_INET
unsigned short sin_port;        //监听的端口号,要建立连接的服务器和客户端的端口必须一致 
struct in_addr sin_addr;        //设置为 INADDR_ANY 表示可以和任何的主机通信。
unsigned char sin_zero[8];
}
  • sockfd 是由 socket 函数调用返回的文件描述符

  • my_addr 是一个指向 sockaddr 的指针

  • addrlen 是 sockaddr 结构的长度

3、 listen 函数

表明服务器可以接受连接请求。成功返回 0,失败返回-1

int listen(int sockfd,int backlog);
  • sockfd 是 bind 后的文件描述符

  • backlog 设置请求排队的最大长度。当有多个客户端程序和服务端相连时,使用这个表示可以介绍的排队长度。

  • listen 函数将 bind 的文件描述符变为监听套接字

4、accept 函数

服务器使用此函数可以获得连接请求,并且建立连接。成功返回 服务器端的文件描述符,失败返回-1

int accept(int sockfd, struct sockaddr *addr,int *addrlen);
  • sockfd 是 listen 后的文件描述符

  • addr,addrlen 是用来给客户端的程序填写的,服务器端只要传递指针就可以了, bind,listen 和 accept 是服务器端用的函数。

  • accept 调用时,服务器端的程序会一直阻塞到有一个客户程序发出了连接。

5、connect 函数

可以用 connect 建立一个连接,在 connect 中所指定的地址是想与之通信的服务器的地址。成功时返回 0,失败时返回-1

int connect(int sockfd, struct sockaddr * serv_addr,int addrlen);
  • sockfd 是 socket 函数返回的文件描述符

  • serv_addr 储存了服务器端的连接信息,其中 sin_add 是服务端的地址

  • addrlen 是 serv_addr 的长度

  • connect 函数是客户端用来同服务端连接的。

6、send 函数

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  • sockfd 指定发送端套接字描述符

  • buf 指明一个存放应用程序要发送数据的缓冲区;

  • len 指明实际要发送的数据的字节数;

  • flags 一般置 0

  • 客户或者服务器应用程序都用 send 函数来向 TCP 连接的另一端发送数据。

7、 recv 函数

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
  • sockfd 指定接收端套接字描述符

  • buf 指明一个缓冲区,该缓冲区用来存放 recv 函数接收到的数据;

  • len 指明 buf 的长度;

  • flags 一般置 0

  • 客户或者服务器应用程序都用 recv 函数从 TCP 连接的另一端接收数据。

四、编写程序

1.服务器端(server.c)

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



#define SERVER_PORT 8888    //端口号
#define BACKLOG 10          //最大允许连接客户端数

int main(int argc,char **argv)
{
    int iSocketServer;      // 接收服务器文件描述符
    int iSocketClient;      // 接收客户端文件描述符
    struct sockaddr_in tSocketServerAddr;   // 主机网络信息结构体
    struct sockaddr_in tSocketClientAddr;   // 客户端网络信息结构体
    int iRet;               // 接收返回值
    int iAddrLen;           // 结构体地址长度
    int iRecvLen;           // 接收数据长度
    int iSendLen;           // 发送数据长度
    int iClientNum = -1;    // 客户端数量
    unsigned char ucRecvBuf[1000];          // 接收数组
    unsigned char ucSendBuf[1000];          // 发送数组
    

    iSocketServer = socket(AF_INET, SOCK_STREAM, 0);    // 建立服务器的套接字文件描述符
    if(-1 == iSocketServer)  // 如果接收到失败
    {
        printf("Socket error\n");
        return -1;
    }

    tSocketServerAddr.sin_family        = AF_INET;
    tSocketServerAddr.sin_port          = htons(SERVER_PORT);    //把主机的字节序转换成网络字节序
    tSocketServerAddr.sin_addr.s_addr   = INADDR_ANY;
    memset(tSocketServerAddr.sin_zero,0,8);

    // 对套接字进行地址和端口的绑定
    iRet = bind(iSocketServer, (struct sockaddr *)&tSocketServerAddr,sizeof(struct sockaddr));
    if(iRet == -1)  // 如果接收到失败
    {
        printf("bind error!\n");
        return -1;
    }

    // 监听服务器,并且允许连接的客户端数量为10
    iRet = listen(iSocketServer, BACKLOG);
    if(iRet == -1)
    {
        printf("listen error!\n");
        return -1;
    }

    while(1)
    {
        iAddrLen = sizeof(struct sockaddr);
        iSocketClient = accept(iSocketServer,(struct sockaddr *)&tSocketClientAddr,&iAddrLen);
        if(iSocketClient != -1)
        {
            printf("connect success!\n");
            iClientNum++;
            printf("get connect from client %d\n",iClientNum);
            if(!fork())     //复制一个子进程
            {
                /* 子进程源码 */
                while(1)
                {
                    /* 接收客户端发来的数据并显示出来 */
                    iRecvLen = recv(iSocketClient,ucRecvBuf,999,0);
                    if(iRecvLen <= 0)
                    {
                        close(iSocketClient);
                        return -1;
                    }
                    else
                    {
                        ucRecvBuf[iRecvLen] = '\0';
                        printf("get mag from client %d: %s\n",iClientNum,ucRecvBuf);
                    }
                }
            }
        }
    }
    close(iSocketServer);
    return 0;
}

2.客户端(client.c)

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

/*
 * socket
 * connect
 * send/rcv
*/
#define SERVER_PORT 8888


int main(int argc,char **argv)
{
    int iSocketClient;
    struct sockaddr_in tSocketServerAddr;

    int iRet;
    int iSenLen;
    int iRecvLen;                           // 接收数据长度
    unsigned char ucRecvBuf[1000];          // 接收数组
    unsigned char ucSendBuf[1000];

    if(argc != 2)
    {
        printf("usage:\n");
        printf("%s <server_ip>\n",argv[0]);
        return -1;
    }

    iSocketClient = socket(AF_INET, SOCK_STREAM, 0);
    if(iSocketClient == -1)
    {   
        printf("socket error!\n");
        return -1;
    }

    tSocketServerAddr.sin_family        = AF_INET;
    tSocketServerAddr.sin_port          = htons(SERVER_PORT);    //把主机的字节序转换成网络字节序
    //tSocketServerAddr.sin_addr.s_addr   = INADDR_ANY;
    if(0 == inet_aton(argv[1],&tSocketServerAddr.sin_addr))
    {
        printf("invalid server_ip\n");
        return -1;
    }
    memset(tSocketServerAddr.sin_zero,0,8);

    iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr,sizeof(struct sockaddr));
    if(iRet == -1)
    {
        printf("connect error!\n");
        return -1;
    }
    
    while(1)
    {
        if(fgets(ucSendBuf, 999, stdin));
        {
            printf("send msg success! msg:%s",ucSendBuf);
            iSenLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);

            if(iSenLen <= 0)
            {
                close(iSocketClient);
                return -1;
            }
        }
    }

    return 0;
}

五、编译测试

1、先在虚拟机编译测试一下

使用以下代码进行编译:

gcc -o 程序名 程序.c

编译完成之后,再新建一个窗口,一个用来运行server,一个用来运行client

可以看到客户端发送信息,服务器接收到了数据。

2、确保虚拟机和开发板的网络连接在同一网络下。

3、接下来我们把客户端(client)程序交叉编译一下,下载到开发板执行(这里如果不知道怎么下载,可以看一下我的上一篇文章)

arm-linux-gnueabi-gcc -o client client.c  //这里交叉编译工具根据自己情况修改

在开发板上先执行以下命令,给予程序可读、可写、可执行的权限。

chmod 777 client

然后在电脑主机运行server程序,之后再在开发板上执行client程序,就可以实现电脑主机和开发板之间的网络通信。

主机执行:./server
客户端执行:./client <服务器IP地址>

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DING_WEI_GUANG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值