build目录下存放gyp目录及内容
2. 服务端编写
代码如下:
#include
#include
#include
#include
#define DEFAULT_PORT 7000
#define DEFAULT_BACKLOG 128
uv_loop_t *loop;
struct sockaddr_in addr;
unsigned int tag_times = 0; // 连续三次收到客户端消息 this is client , 则结束通信
typedef struct {
uv_write_t req;
uv_buf_t buf;
} write_req_t;
// 获取客户端ip, port
void check_sockname(struct sockaddr* addr, const char* compare_ip, int compare_port, const char* context) {
struct sockaddr_in check_addr = *(struct sockaddr_in*) addr;
char check_ip[17] = { 0 };
int r;
struct sockaddr_in compare_addr = { 0 };
int ret = uv_ip4_addr(compare_ip, compare_port, &compare_addr);
if (check_addr.sin_family == AF_INET) {
printf("src sin_family is AF_INET.\n");
}
if (compare_addr.sin_family == AF_INET) {
printf("compare sin_family is AF_INET.\n");
}
if (memcmp(&check_addr.sin_addr, &compare_addr.sin_addr,
sizeof compare_addr.sin_addr) == 0) {
printf("ip matches.\n");
}
if (compare_port == 0 || check_addr.sin_port == compare_addr.sin_port) {
printf("port matches.\n");
}
//网络字节序转换成主机字符序
uv_ip4_name(&check_addr, (char*)check_ip, sizeof check_ip);
printf("%s: %s:%d\n", context, check_ip, check_addr.sin_port);
}
void free_write_req(uv_write_t *req) {
write_req_t *wr = (write_req_t*)req;
free(wr->buf.base);
free(wr);
}
void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
buf->base = (char*)malloc(suggested_size);
buf->len = suggested_size;
}
void on_close(uv_handle_t* handle) {
free(handle);
}
void echo_write(uv_write_t *req, int status) {
if (status) {
fprintf(stderr, "Write error %s\n", uv_strerror(status));
}
free_write_req(req);
}
void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) {
if (nread > 0) {
write_req_t *req = (write_req_t*)malloc(sizeof(write_req_t));
req->buf = uv_buf_init(buf->base, nread);
fwrite(buf->base, nread, 1, stdout); // 控制台显示客户端发送过来的内容
fwrite("\n", 1, 1, stdout);
if (strcmp(req->buf.base, "are you ok")==0) {
char ack_msg[] = "server ok";
strcpy(req->buf.base, ack_msg);
req->buf.len = strlen(ack_msg);
uv_write((uv_write_t*)req, client, &req->buf, 1, echo_write); // 往客户端发送内容
}
else if (strcmp(req->buf.base, "this is client") == 0) { // 连续三次收到 this is client 消息,则发送结束标志 finished
tag_times++;
if (tag_times == 3)
{
char ack_msg[] = "finished";
strcpy(req->buf.base, ack_msg);
req->buf.len = strlen(ack_msg);
uv_write((uv_write_t*)req, client, &req->buf, 1, echo_write); // 往客户端发送内容
}
else {
uv_write((uv_write_t*)req, client, &req->buf, 1, echo_write); // 往客户端发送内容
}
}
else {
uv_write((uv_write_t*)req, client, &req->buf, 1, echo_write); // 往客户端发送内容
}
return;
}
if (nread < 0) {
if (nread != UV_EOF)
fprintf(stderr, "Read error %s\n", uv_err_name(nread));
uv_close((uv_handle_t*)client, on_close);
}
free(buf->base);
}
void on_new_connection(uv_stream_t *server, int status) {
if (status < 0) {
fprintf(stderr, "New connection error %s\n", uv_strerror(status));
// error!
return;
}
uv_tcp_t *client = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));
uv_tcp_init(loop, client);
//client->data = server;
if (uv_accept(server, (uv_stream_t*)client) == 0) {
//获取客户端 ip和端口
//{
// struct sockaddr peername;
// int namelen, r;
//
// //有连接,可以获得目标的ip和端口
// namelen = sizeof peername;
// r = uv_tcp_getpeername(client, &peername, &namelen);
// printf("the r is %d.\n", r);
// check_sockname(&peername, "127.0.0.1", -1, "accepted socket peer");
//}
uv_read_start((uv_stream_t*)client, alloc_buffer, echo_read);
}
else {
uv_close((uv_handle_t*)client, on_close);
}
}
int main()
{
loop = uv_default_loop();
uv_tcp_t server;
uv_tcp_init(loop, &server);
uv_ip4_addr("192.168.0.25", DEFAULT_PORT, &addr);
uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);
int r = uv_listen((uv_stream_t*)&server, DEFAULT_BACKLOG, on_new_connection);
if (r) {
fprintf(stderr, "Listen error %s\n", uv_strerror(r));
return 1;
}
return uv_run(loop, UV_RUN_DEFAULT);
}
3. 客户端编写
#include
#include
#pragma comment(lib, "ws2_32.lib")
#define NO_FLAGS_SET 0
#define PORT (u_short) 7000
#define DEST_IP_ADDR "192.168.0.25" //Server address
int main()
{
// initial socket library
WORD wVerisonRequested;
WSADATA wsaData;
int err;
wVerisonRequested = MAKEWORD(1, 1);
err = WSAStartup(wVerisonRequested, &wsaData);
if (err != 0)
{
return -1;
}
// create socket
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
// connect server socket
SOCKADDR_IN addrServer;
addrServer.sin_addr.S_un.S_addr = inet_addr(DEST_IP_ADDR);
addrServer.sin_family = AF_INET;
addrServer.sin_port = htons(PORT);
connect(sockClient, (SOCKADDR *)&addrServer, sizeof(addrServer));
// send
char sendBuf[ ] = "are you ok";
int sended = send(sockClient, sendBuf, strlen(sendBuf) + 1, 0);
// receive
char recvBuf[MAX_PATH] = { 0 };
recv(sockClient, recvBuf, sizeof(recvBuf), 0);
if (strcmp(recvBuf, "server ok") == 0) {
printf("%s\n", recvBuf);
bool is_finished = false;
while (!is_finished) {
char sendBuf[] = "this is client";
int sended = send(sockClient, sendBuf, strlen(sendBuf) + 1, 0);
::Sleep(50);
char recvBuf[MAX_PATH] = { 0 };
recv(sockClient, recvBuf, sizeof(recvBuf), 0);
printf("%s\n", recvBuf);
if (strcmp(recvBuf, "finished") == 0) {
is_finished = true;
}
}
}
printf("%s\n", recvBuf);
closesocket(sockClient);
WSACleanup();
system("pause");
return 0;
}
4. 相关函数解释
服务端: 一般监听时设置 连接到来的回调 ,如下:
int r = uv_listen((uv_stream_t*)&server, DEFAULT_BACKLOG, on_new_connection);
4.1 on_new_connection 如何被调用呢?
答:在tcp.c 文件的 函数 uv_process_tcp_accept_req 中执行了连接到来的回调。
4.2 在连接到来回调函数内部,指定了读到来回调,uv_read_start((uv_stream_t*)client, alloc_buffer, echo_read); 其中:echo_read回调如何被执行呢?
答: 由tcp.c 中 uv_process_tcp_read_req 函数内部调用了读回调。
4.3 服务端写消息函数 uv_write((uv_write_t*)req, client, &req->buf, 1, echo_write);
其中:echo_write 回调如何被执行呢?
答:在tcp.c中,函数 uv_process_tcp_write_req 内部执行了回调echo_write