客户端的流程如下:socket()→connect()→recv()→send()→close()
下面是一个简单的echo代码。
1)由于buff限制,recv函数一次无法完全接收完所有数据,可以判断返回值。循环调用recv进行接收。
2)如果recv的返回值为0,说明连接被断开。
#include <FreeRTOS.h>
#include <task.h>
#include "sys/types.h"
#include "sys/socket.h"
#include "arpa/inet.h"
#include "stdio.h"
#include "unistd.h"
#include "string.h"
int tcp_client_test(void)
{
int sock = 0;
struct sockaddr_in addr = { 0 };
int len = 0;
int r = 0;
char buf[128] = { 0 };
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1) {
log_e("socket error");
return -1;
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("192.168.1.10");
addr.sin_port = htons(8888);
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
log_e("connect error");
return -1;
}
log_i("connect success");
do {
r = recv(sock, buf, sizeof(buf), 0);
send(sock, buf, r, 0);
log_hex_dump("tcp", 32, buf, r);
} while (r != 0);
close(sock);
return 0;
}
服务端需要先用一个socket获取连接的socket,再用获取的socket去进行实际的通讯。
1)服务端socket只用于接收连接不进行实际通信。
2)绑定(bind) socket之后用listen进入阻塞状态,监听是否有连接。
3)监听到有连接之后,accept()函数返回与客户端通信的socket。这个才是实际进行通讯的socket。
#include <FreeRTOS.h>
#include <task.h>
#include "sys/types.h"
#include "sys/socket.h"
#include "arpa/inet.h"
#include "stdio.h"
#include "unistd.h"
#include "string.h"
/**
* @brief 1.服务端socket只用于接收连接不进行实际通信.
* 2.accept()函数返回与客户端通信的socket.
*
* socket究竟是什么?
* @return {*}
*/
int tcp_server_test(void)
{
int ret = -1;
int server = 0;
int client = 0;
struct sockaddr_in saddr = { 0 };
struct sockaddr_in caddr = { 0 };
socklen_t asize = 0;
int len = 0;
char buf[32] = { 0 };
int r = 0;
do {
/* 1.新建socket */
server = socket(PF_INET, SOCK_STREAM, 0);
if (server == -1) {
log_e("socket error");
break;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(8899);
/* 2.绑定 bind(sock,*addr, addrlen) */
if (bind(server, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
log_e("bind error");
break;
}
/* 3.监听 listen(sock, backlog) 第二个参数是监听队列大小 */
if (listen(server, 1) == -1) {
}
log_i("server start success!");
/* 4.接受请求 accept(sock,*addr, addrlen) */
asize = sizeof(caddr);
client = accept(server, (struct sockaddr *)&caddr, &asize);
if (client == -1) {
log_e("accept error");
break;
}
log_i("client:%d connect success!", client);
len = 0;
do {
r = recv(client, buf, sizeof(buf), 0);
log_hex_dump("client", 32, buf, r);
if (r > 0) {
len += r;
}
} while (len < 64);
send(client, "Hello World!", 12, 0);
vTaskDelay(1000 / portTICK_PERIOD_MS);
close(server);
close(client);
} while (0);
return ret;
}
socket是一个多功能的函数,返回值是资源标识符。
close函数释放资源。
#include "arpa/inet.h"
头文件中有很多处理地址的函数,我以前不知道,傻傻的自己处理。
htonl
,htons
函数将本机字节序转换为网络字节序。
inet_addr
将IP字符串转换成符合网络字节序的整数。