android 监听本机网络请求_Linux网络应用编程TCP

1.TCP

TCP有专门的传递保证机制,收到数据时会自动发送确认消息,发送方收到确认消息后才会继续发送消息,否则继续等待。这样的好处是传输的数据是可靠的,此外它是有连接的传输,大多数网络传输都是用的TCP。

1.1 TCP流程图

9740918c9cd840bfbcd1bac89b259119

1.2 TCP步骤分析

程序分为服务器端和客户机端,先从服务器端开始分析。

服务器端:

a. 创建socket

if (-1 == sock_fd){        fprintf(stderr,"socket error:%sa", strerror(errno));        exit(1);}

所需要头文件:

#include #include 

函数格式

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

函数功能:

创建一个套接字;

domain:协议域(族),决定了套接字的地址类型,例如AF_INET决定了要用IPv4地址(32位)与端口号(16位)的组合。常见的协议族有:AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX)、AF_ROUTE等;

type:指定套接字类型,SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)、SOCK_RAW

protocol:指定socket所使用的传输协议编号,通常为0

返回值:

若成功,返回一个套接字描述符,否则返回-1;

Socket就是一种文件描述符,和普通的打开文件一样,需要检测其返回结果。

b. 设置socket

memset(&server_addr, 0, sizeof(struct sockaddr_in));//clearserver_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY:This machine all IPserver_addr.sin_port = htons(PORT_NUMBER);

设置何种协议族,设置本机IP和端口,也就有了唯一性。

c. 绑定socket

ret = bind(sock_fd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr));    if(-1 == ret)    {        fprintf(stderr,"bind error:%sa", strerror(errno));        close(sock_fd);        exit(1);}

所需要头文件:

#include #include 

函数格式

int bind(int sockfd, struct sockaddr *addr, int addrlen);

函数功能:

把套接字绑定到本地计算机的某一个端口上;

sockfd:待绑定的套接字描述符

addr:一个struct sockaddr *指针,指定要绑定给sockfd的协议地址。内容结构由前面的协议族决定。

addrlen:地址的长度

返回值:

若成功,返回0,否则返回-1,错误信息存在errno中;

d. 开始监听

ret = listen(sock_fd, BACKLOG);    if (-1 == ret)    {        fprintf(stderr,"listen error:%sa", strerror(errno));        close(sock_fd);        exit(1);    }

所需要头文件:

#include #include 

函数格式

int listen(int sockfd, int backlog);

函数功能:

使服务器的这个端口和IP处于监听状态,等待网络中某一客户机的连接请求,最大连接数量为backlog≤128;

sockfd:待监听的套接字描述符

backlog:最大可监听和连接的客户端数量

返回值:

若成功,返回0,否则返回-1;

e. 阻塞,等待连接

addr_len = sizeof(struct sockaddr);        new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &addr_len);        if (-1 == new_fd)        {            fprintf(stderr,"accept error:%sa", strerror(errno));            close(sock_fd);            exit(1);        }

所需要头文件:

#include #include 

函数格式

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

函数功能

接受连接请求,建立起与客户机之间的通信连接。服务器处于监听状态时,如果某时刻获得客户机的连接请求,此时并不是立即处理这个请求,而是将这个请求放在等待队列中,当系统空闲时再处理客户机的连接请求;

当accept函数接受一个连接时,会返回一个新的socket标识符,以后的数据传输和读取就要通过这个新的socket编号来处理,原来参数中的socket也可以继续使用,继续监听其它客户机的连接请求;

accept连接成功时,参数addr所指的结构体会填入所连接机器的地址数据;

sockfd:待监听的套接字描述符

addr:指向struct sockaddr的指针,用于返回客户端的协议地址

addrlen:协议地址的长度

返回值:

若成功,返回一个由内核自动生成的一个全新描述字,代表与返回客户的TCP连接,否则返回-1,错误信息存在errno中;

f. 接收数据

recv_len = recv(new_fd, recv_buf, 999, 0);    if (recv_len <= 0)    {        fprintf(stderr, "recv error:%sa", strerror(errno));        close(new_fd);        exit(1);    }    else    {        recv_buf[recv_len] = '0';        printf("Get msg from client%d: %s", client_num, recv_buf);    }

所需要头文件:

#include #include 

函数格式

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

函数功能:

用新的套接字来接收远端主机传来的数据,并把数据存到由参数buf指向的内存空间;

sockfd:sockfd为前面accept的返回值,即new_fd,也就是新的套接字

buf:指明一个缓冲区

len:指明缓冲区的长度

flags:通常为0

返回值:

若成功,返回接收到的字节数,另一端已关闭则返回0,否则返回-1,错误信息存在errno中;

g. 关闭socket

    exit(0); 

为了应对多个连接,并保证它们之间相互独立,实际编程中往往还要加入多进程fork()。让子进程接收数据,父进程继续监听新的连接。

  • 客户机端:

a. 创建socket

sock_fd = socket(AF_INET, SOCK_STREAM, 0);//AF_INET:IPV4;SOCK_STREAM:TCP    if (-1 == sock_fd)    {        fprintf(stderr,"socket error:%sa", strerror(errno));        exit(1);    }

b. 设置socket

memset(&server_addr, 0, sizeof(struct sockaddr_in));//clearserver_addr.sin_family = AF_INET;server_addr.sin_port = htons(PORT_NUMBER);

其中注意的是,这里设置的socket内容是指 希望连接的服务器IP和端口号信息,IP地址来自用户的输入,并转换格式得到。因此,这里的设置和服务器的设置,要保持内容上的一致。

ret = inet_aton(argv[1], &server_addr.sin_addr);    if(0 == ret)    {        fprintf(stderr,"server_ip error.");        close(sock_fd);        exit(1);    }

c. 连接

ret = connect(sock_fd, (const struct sockaddr *)&server_addr, sizeof(struct sockaddr));    if (-1 == ret)    {        fprintf(stderr,"connect error:%sa", strerror(errno));        close(sock_fd);        exit(1);    }

所需要头文件:

#include `#include 

函数格式

int connect (int sockfd, struct sockaddr *serv_addr, int addrlen);

函数功能:

用来请求连接远程服务器,将参数sockfd的socket连至参数serv_addr所指定的服务器IP和端口号上去;

sockfd:客户端的socket套接字

serv_addr:一个struct sockaddr类型的结构体指针变量,存储着远程服务器的IP与端口号信息

addrlen:结构体变量的长度

返回值:

若成功,返回0,否则返回-1,错误信息存在errno中;

d. 发送

send_buf = send(sock_fd, send_buf, strlen(send_buf), 0);if (send_buf <= 0){fprintf(stderr,"send error:%sa", strerror(errno));close(sock_fd);exit(1);}

所需要头文件:

#include #include 

函数格式

int send(int sockfd, const void *buf, int len, int flags);

函数功能:用来发送数据给指定的远端主机;sockfd:客户端的socket套接字buf:指明一个缓冲区len:指明缓冲区的长度flags:通常为0

返回值:若成功,返回发送的字节数,否则返回-1,错误信息存在errno中

d. 关闭socket

    close(sock_fd);    exit(0);

1.3 TCP完整代码

/** tcp_server.c# Copyright (C) 2017 hceng, # Licensed under terms of GPLv2## This program is used for TCP / UDP learning.# https://hceng.cn/*/#include #include #include       #include #include #include #include #include #include #include #include #define PORT_NUMBER 8888#define BACKLOG     10/* socket->bind->listen->accept->send/recv->close*/int main(int argc, char **argv){    int sock_fd, new_fd;    struct sockaddr_in server_addr;    struct sockaddr_in client_addr;    int ret;    int addr_len;    int recv_len;    unsigned char recv_buf[1000];    int client_num = -1;        signal(SIGCHLD,SIG_IGN);    /* socket */    sock_fd = socket(AF_INET, SOCK_STREAM, 0);//AF_INET:IPV4;SOCK_STREAM:TCP    if (-1 == sock_fd)    {        fprintf(stderr,"socket error:%sa", strerror(errno));        exit(1);    }        /* set server sockaddr_in */    memset(&server_addr, 0, sizeof(struct sockaddr_in));//clear    server_addr.sin_family = AF_INET;    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY:This machine all IP    server_addr.sin_port = htons(PORT_NUMBER);    /* bind */    ret = bind(sock_fd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr));    if(-1 == ret)    {        fprintf(stderr,"bind error:%sa", strerror(errno));        close(sock_fd);        exit(1);    }        /* listen */    ret = listen(sock_fd, BACKLOG);    if (-1 == ret)    {        fprintf(stderr,"listen error:%sa", strerror(errno));        close(sock_fd);        exit(1);    }        /* accept */    while(1)    {        addr_len = sizeof(struct sockaddr);        new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &addr_len);        if (-1 == new_fd)        {            fprintf(stderr,"accept error:%sa", strerror(errno));            close(sock_fd);            exit(1);        }                client_num++;        fprintf(stderr, "Server get connetion form client%d: %s", client_num, inet_ntoa(client_addr.sin_addr));                if (!fork()){            /* Child process */            while (1)            {                /* recv */                recv_len = recv(new_fd, recv_buf, 999, 0);                if (recv_len <= 0)                {                    fprintf(stderr, "recv error:%sa", strerror(errno));                    close(new_fd);                    exit(1);                }                else                {                    recv_buf[recv_len] = '0';                    printf("Get msg from client%d: %s", client_num, recv_buf);                }            }            close(new_fd);        }       }        /* close */    close(sock_fd);    exit(0); }
/** tcp_client.c# Copyright (C) 2017 hceng, # Licensed under terms of GPLv2## This program is used for TCP / UDP learning.# https://hceng.cn/*/#include #include #include #include #include #include #include #include #include #include #define PORT_NUMBER 8888/* socket->connect->send->close*/int main(int argc, char *argv[]){    int sock_fd;    struct sockaddr_in server_addr;    int ret;    unsigned char send_buf[1000];    int send_len;        if(argc != 2)    {        fprintf(stderr, "Usage:%s hostnamea", argv[0]);        exit(1);    }        /* socket */    sock_fd = socket(AF_INET, SOCK_STREAM, 0);//AF_INET:IPV4;SOCK_STREAM:TCP    if (-1 == sock_fd)    {        fprintf(stderr,"socket error:%sa", strerror(errno));        exit(1);    }        /* set sockaddr_in parameter*/    memset(&server_addr, 0, sizeof(struct sockaddr_in));//clear    server_addr.sin_family = AF_INET;    server_addr.sin_port = htons(PORT_NUMBER);    ret = inet_aton(argv[1], &server_addr.sin_addr);    if(0 == ret)    {        fprintf(stderr,"server_ip error.");        close(sock_fd);        exit(1);    }        /* connect */    ret = connect(sock_fd, (const struct sockaddr *)&server_addr, sizeof(struct sockaddr));    if (-1 == ret)    {        fprintf(stderr,"connect error:%sa", strerror(errno));        close(sock_fd);        exit(1);    }        while (1)    {        if (fgets(send_buf, 999, stdin))        {            /* send */            send_len = send(sock_fd, send_buf, strlen(send_buf), 0);            if (send_len <= 0)            {                fprintf(stderr,"send error:%sa", strerror(errno));                close(sock_fd);                exit(1);            }        }    }        /* close */    close(sock_fd);    exit(0);}

1.4 测试结果

先在Ubuntu主机上交叉编译服务器端代码,再在Ubuntu主机上编译客户端代码。在开发板上运行服务器端代码,在Ubuntu主机先启动tmux分屏,再分别运行客户端代码。

服务器端

130a092203bc4821bada13855c1e9637

「新品首发」STM32MP157开发板火爆预售!首批仅300套

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值