我在之前的文章中曾说过,我现在开始负责车辆以太网应用层协议栈的开发和维护。
以太网通信说白了也属于计算机通信的范畴,因此网络通信的5层模型仍然适用于车辆通信领域。
既然设计到ECU间的网络通信,那么socket网络编程的知识一定是必不可少的。
今天这篇文章就总结我这周学习的socket网络编程的知识。
Socket编程常用的函数
首先,下图是client与server之间的通信流程图。
根据上面的socket通信流程图,socket编程常用的函数如下:
-
客户端使用socket(), connect(), send(), recv(), close();
-
服务端使用socket(), bind(), listen(), accept(), recv(), send(), close();
Socket通信的三次握手和四次挥手
Socket建立连接三次握手的流程
-
第一次握手:client端先向server端发送连接请求
-
第二次握手:server收到后,回复ACK给client
-
第三次握手:client端收到ACK后,回复ACK给server
至此,client与server之间的网络连接建立,可以顺利进行数据传输。
Socket断开连接四次挥手的流程
-
第一次挥手:client向server发送断开连接请求
-
第二次挥手:server收到client的请求,回复ACK给client (这次挥手表示server收到了client的请求,但是并不会立即断开连接,可能还有未发送的数据)
-
第三次挥手:server向client发送断开连接请求(这次挥手表示server端的数据已经发送完毕,client端可以断开)
-
第四次挥手:client收到了server的请求,发送ACK给server。之后client端处于等待状态,过了一段时间后将socket关闭
至此,client与server之间的网络连接断开。
简单实现进程间的Socket的通信
实现步骤如下:
(1)使用socket套接字,并选择TCP/IP协议,端口号为6000
(2)客户端发送“this is a test”到服务端
(3)服务端收到后打印字符串,并回复“test ok”到客户端
(4)通信结束,断开连接
客户端socket通信的实现步骤
-
socket()创建socket
-
设置socket的属性
-
connect()向服务端发起连接
-
send()向服务端发送消息
-
recv()等待服务端的消息
-
close()关闭socket,回收资源
//client.c
#include<stdio.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<netinet/in.h>
#define PORT 6000
int main()
{
int socketfd, error;
struct sockaddr_in serv_addr;
//创建socket
socketfd = socket(AF_INET, SOCK_STREAM, 0);
//设置socket的属性
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
serv_addr.sin_addr.s_addr = INADDR_ANY;
//向服务端发起连接
error = connect(socketfd, &serv_addr, sizeof(struct sockaddr));
if(0 != error)
{
printf("connect server error.");
return 0;
}
//向服务端发送消息
char *buff = "this is a test";
error = send(socketfd, buff, strlen(buff), 0);
if(-1 == error)
{
printf("send message to server error.");
return 0;
}
//等待接收服务端的消息
char recv_buff[1024] = {0};
error = recv(socketfd, recv_buff, 1024,0);
if(-1 == error)
{
printf("receive error");
return 0;
}
printf("Receive buff:%s\n", recv_buff);
//关闭socket,回收资源
close(socketfd);
return 0;
}
服务端socket通信的实现步骤
- socket()创建socket
- bind()对socket进行绑定
- listen()监听绑定的socket,用于监听来自客户端的消息
- accept()用于接收客户端的连接请求和消息
- recv()函数接收客户端的消息,并将其保存到设置好的buffer中
- send()函数用于服务端向客户端发送消息
- close()关闭socket,回收资源
//server.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define PORT 6000
int main()
{
int socketfd, clientfd, error;
struct sockaddr_in server_addr, client_addr;
int sin_size;
char buff[1024] = {0};
//创建socket
socketfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == socketfd)
{
printf("create socket fail.");
return 0;
}
//socket属性设置
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = INADDR_ANY;
//绑定socket
error = bind(socketfd, &server_addr, sizeof(struct sockaddr_in));
if(0 != error)
{
printf("bind fail.");
close(socketfd);
return 0;
}
//监听socket
error = listen(socketfd, 5);
if(0 != error)
{
printf("listen fail.");
close(socketfd);
return 0;
}
//接收客户端的连接请求
clientfd = accept(socketfd, (struct sockaddr*)&client_addr, &sin_size);
//连接建立后,接收客户端的消息
error = recv(clientfd, buff, 1024,0);
if(-1 == error)
{
printf("receive error");
close(socketfd);
return 0;
}
printf("Receive buff:%s\n", buff);
//收到消息后,向客户端发送响应
char *send_buff = "test ok.";
error = send(clientfd, send_buff, strlen(send_buff), 0);
if(-1 == error)
{
printf("send error");
close(socketfd);
return 0;
}
//关闭socket,回收资源
close(socketfd);
return 0;
}
代码完成后,使用gcc编译代码,生成server和client的可执行文件。
然后,在linux界面打开两个命令行,先运行server进程,再运行client进程,即可观察到两个进程之间的通信。
总结
这篇文章承接的是之前《白话计算机网络》,但当时工作较忙一直没有更新。
此外,如果想继续学习socket通信和网络协议,推荐一个开源项目tinyhttp。该项目通过500行代码,利用多线程和socket通信简单实现了http协议,是很适合初学者学习的资源,强烈推荐!!!
我这两天正在阅读该项目的源码,等把项目源码吃透,会写一篇总结出来。
参考资料:
https://www.cnblogs.com/niwotaxuexiba/p/9700764.html
https://blog.csdn.net/chenlycly/article/details/51657179
ps: 欢迎关注我的公众号[酷酷的coder],分享转行菜鸟程序员成长过程汇总的烦恼和反思.