8.3 TCP编程示例
本节源码位于如下目录:
8.3.1
客户端程序
client.c代码如下,逐段分析。
15
定义了服务器端口号为8888,这是客户端将要连接的端口。
17 int main(int argc, char **argv)
18 {
19 int iSocketClient;
20 struct sockaddr_in tSocketServerAddr;
21
22 int iRet;
23 unsigned char ucSendBuf[1000];
24 int iSendLen;
声明了一些变量:iSocketClient用于存储套接字描述符,tSocketServerAddr用于存储服务器地址信息,iRet用于存储函数返回值,ucSendBuf是发送缓冲区,iSendLen用于存储发送的字节数。
26 if (argc != 2)
27 {
28 printf("Usage:\n");
29 printf("%s
\n" , argv[0]);30 return -1;
31 }
检查命令行参数数量是否正确。如果不是两个参数(程序名和服务器IP),则打印使用方法并退出程序。
33 iSocketClient = socket(AF_INET, SOCK_STREAM, 0);
调用socket函数创建一个新的套接字,AF_INET表示使用IPv4地址,SOCK_STREAM表示使用TCP协议。
35 tSocketServerAddr.sin_family = AF_INET;
36 tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short*/
设置服务器地址结构体,指定地址族和端口号。htons函数将端口号从主机字节序转换为网络字节序。
37 //tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
38 if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))
39 {
40 printf("invalid server_ip\n");
41 return -1;
42 }
注释掉的行是一个备选项,用于绑定到任何地址。inet_aton函数将命令行中给定的IP地址字符串转换为网络字节序的二进制形式。如果转换失败,打印错误信息并退出。
43 memset(tSocketServerAddr.sin_zero, 0, 8);
清零结构体中的sin_zero成员,这是为了与早期的结构体保持兼容性。
46 iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
47 if (-1 == iRet)
48 {
49 printf("connect error!\n");
50 return -1;
51 }
尝试连接到服务器。如果连接失败,打印错误信息并退出。
53 while (1)
54 {
55 if (fgets(ucSendBuf, 999, stdin))
56 {
57 iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
58 if (iSendLen <= 0)
59 {
60 close(iSocketClient);
61 return -1;
62 }
63 }
64 }
无限循环,从标准输入读取数据并发送到服务器。如果发送失败,关闭套接字并退出。
8.3.2
服务端程序
server.c代码如下,逐段分析。
19
20
定义了服务器端口号为8888和监听队列的最大长度为 10。
22 int main(int argc, char **argv)
23 {
24 int iSocketServer;
25 int iSocketClient;
26 struct sockaddr_in tSocketServerAddr;
27 struct sockaddr_in tSocketClientAddr;
28 int iRet;
29 int iAddrLen;
30
31 int iRecvLen;
32 unsigned char ucRecvBuf[1000];
33
34 int iClientNum = -1;
35
36 signal(SIGCHLD,SIG_IGN);
声明变量和设置信号处理。signal(SIGCHLD,SIG_IGN)用于忽略子进程结束信号,防止出现僵尸进程。
38 iSocketServer = socket(AF_INET, SOCK_STREAM, 0);
39 if (-1 == iSocketServer)
40 {
41 printf("socket error!\n");
42 return -1;
43 }
创建一个TCP套接字,如果创建失败则打印错误信息并退出。
45 tSocketServerAddr.sin_family = AF_INET;
46 tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short*/
47 tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
48 memset(tSocketServerAddr.sin_zero, 0, 8);
设置服务器地址结构体,指定IPv4地址族、端口号和接受任意IP地址的连接。
50 iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
51 if (-1 == iRet)
52 {
53 printf("bind error!\n");
54 return -1;
55 }
将套接字绑定到指定的端口上,如果绑定失败则打印错误信息并退出。
57 iRet = listen(iSocketServer, BACKLOG);
58 if (-1 == iRet)
59 {
60 printf("listen error!\n");
61 return -1;
62 }
开始监听端口,等待客户端的连接。
64 while (1)
65 {
66 iAddrLen = sizeof(struct sockaddr);
67 iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
68 if (-1 != iSocketClient)
69 {
70 iClientNum++;
71 printf("Get connect from client %d : %s\n", iClientNum, inet_ntoa(tSocketClientAddr.sin_addr));
72 if (!fork())
73 {
74 /* 子进程代码 */
75 while (1)
76 {
77 /* 接收客户端发送的数据并显示出来 */
78 iRecvLen = recv(iSocketClient, ucRecvBuf, 999, 0);
79 if (iRecvLen <= 0)
80 {
81 close(iSocketClient);
82 return -1;
83 }
84 else
85 {
86 ucRecvBuf[iRecvLen] = '\0';
87 printf("Get Msg From Client %d: %s\n", iClientNum, ucRecvBuf);
88 }
89 }
90 }
91 }
92 }
无限循环,接受客户端的连接。对于每个连接,创建一个新的进程来处理该连接。在子进程中,接收客户端发送的数据并打印出来。如果接收失败,关闭套接字并退出。
94 close(iSocketServer);
95 return 0;
关闭服务器套接字并正常退出程序。
8.3.3
上机实验
编译:
source /opt/remi-sdk/environment-setup-aarch64-poky-linux
$CC client.c -o client
$CC server.c -o server
假设设置开发板的IP为:192.168.5.9,上传程序到开发板上
scp ./client root@192.168.5.9:/mnt/
scp ./server root@192.168.5.9:/mnt/
测试:进入/mnt目录运行程序:
root@myir-remi-1g:~
root@myir-remi-1g:/mnt
client server
后台运行服务端程序并运行客户端监听本地IP。
root@myir-remi-1g:/mnt# ./server &
root@myir-remi-1g:/mnt# ./client 127.0.0.1
Get connect from client 0 : 127.0.0.1
nihao
Get Msg From Client 0: nihao
运行程序后,在终端输入任意字符后按下回车可以看到客户端获得我们输入的字符。
如您在使用瑞萨MCU/MPU产品中有任何问题,可识别下方二维码或复制网址到浏览器中打开,进入瑞萨技术论坛寻找答案或获取在线技术支持。
https://community-ja.renesas.com/zh/forums-groups/mcu-mpu/
未完待续
推荐阅读
操作像素的程序分析与基于Framebuffer的字符显示 - RZ MPU工业控制教程连载(14)
在Framebuffer上显示图片 - RZ MPU工业控制教程连载(15)
网络通信概述 - RZ MPU工业控制教程连载(16)