之间在网上看到很多网络编程都是一个一个demo,今天我把之前学到的汇总起来,希望大家可以进行补充。
我理解的网络通信分为4种
1,udp客户端
2,udp服务端
3,tcp客户端
4,tcp服务端
线程中我使用过两种方式编程,一种是经典函数式编程加上标志位,如下:
while(1)
{
server_init();
client_init();
sock_send();
select_handler();
}
其中各函数里面放置了大量的标志位,如下:
void client_init(void)
{//确认客户端初始化标志位//}void sock_send(void)
{//判断客户端标志位,成功则继续进行
if(client_init_flag)
{//发送操作//确认客户端发送标志位
}
}void select_handler(void)
{if(send_flag)
{//处理数据并接受可以用select
}
}
这种方式,我觉得在看代码的时候很乱,但是他在大量的通信时还比较友好,可以建立一个结构体数组,每个数组成员代表一个客户端,结构体放置client_init_flag和send_flag。
还有一种方式采用的是状态机编程
创建枚举
typedef enumclient_statues_t
{
init_flag_status,
send_flag_status,
}client_statues_t;
client_statues_t client_statues;while(1)
{//先接受,后发送
switch(client_statues)
{caseinit_flag_status:
client_init();break;casesend_flag_status:
sock_send();break;
。。。
}
}
状态机在单片机使用很常见,但是如果多客户端初始化与发送,容易搞混,并且个人觉得增删查改略费劲,有可能是我自己水平有限,所以今天只写了一个关于第一种方式的代码。因为标志位太多了,而且各种判断比较乱,所以以下就没有各种标志位,但真正项目中是要有的并且还需要有打印日志功能(printf函数),整体思路如下:
>>创建udp服务端,创建tcp服务端>>创建udp客户端,创建tcp客户端>>发送数据>>接受数据并处理
首先是udp服务端,创建tcp服务端,服务端程序比客户端较简单
//返回值为是否成功标志,需要在各行赋值代码中判断,此代码不进行演示//create_udpServer()、create_tcpServer()参数可以为全局变量的关于服务器端的结构体,结构体里端口号,初始化标志位,等等,此代码不进行演示
structsockaddr_in udp_sockServer;void create_udpServer(void)
{
udp_socket= socket(AF_INET, SOCK_DGRAM,0);//INADDR_ANY,
udp_sockServer.sin_addr.s_addr =htonl(INADDR_ANY);
udp_sockServer.sin_port= htons(udp_port);//port自己设置
udp_sockServer.sin_family =AF_INET;
bind(udp_socket, (struct sockaddr *)&udp_sockServer, sizeof(structsockaddr_in));
}structsockaddr_in tcp_sockServer;void create_tcpServer(void)
{
tcp_socket= socket(AF_INET, SOCK_STREAM, 0);
tcp_sockServer.sin_addr.s_addr=htonl(INADDR_ANY);
tcp_sockServer.sin_port= htons(tcp_podt);//port自己设置
tcp_sockServer.sin_family =AF_INET;
bind(tcp_socket, (struct sockaddr *)&tcp_sockServer, sizeof(structsockaddr_in));
listen(tcp_socket, n);//n自己设置
}void server_init(void)
{
create_udpServer();
create_tcpServer();
}
其次是创建tcp和udp的客户端
//返回值为是否成功标志,需要在各行赋值代码中判断,此代码不进行演示//create_tcpClient()、create_udpClient(v)参数可以为全局变量的关于客户端的结构体,要连接服务器的结构体信息,初始化标志位,等等,此代码不进行演示
structsockaddr_in tcpServer;void create_tcpClient(void)
{
tcpSock= socket(AF_INET, SOCK_STREAM, 0);//客户端需要知道服务端信息
tcpServer.sin_addr.s_addr = htonl(ip);//ip自己设置
tcpServer.sin_port = htons(tcpPort);//port自己设置
tcpServer.sin_family =AF_INET;/*设置setsockopt参数 ,具体请看我之间发的一篇blog
*https://blog.csdn.net/qq_32166779/article/details/88853435
*/}/*把connect单独写是因为这个步骤比较特殊,他是阻塞函数,需要有一定的延时,有两种方、法,一种是利用setsockopt:https://blog.csdn.net/qq_32166779/article/details/88853435一种是利用select检查socket描述符:http://blog.csdn.net/ast_224/article/details/2957294
*/
void tcpClient_connectServer(void)
{//这个tcpsock和create_tcpClient函数中是同一个
connect(tcpSock, (struct sockaddr *)&tcpServer, sizeof(structsockaddr_in));
}structsockaddr_in udpServer;void create_udpClient(void)
{
udpSock= socket(AF_INET, SOCK_DGRAM, 0);
udpServer.sin_addr.s_addr= htonl(pUDP->ip);
udpServer.sin_port= htons(pUDP->udpPort);
udpServer.sin_family=AF_INET;
}void client_init(void)
{
create_tcpClient();
tcpClient_connectServer();
create_udpClient();
}
udp 服务器接受与发送函数,这个只设置能接受一个客户端发数
//udp先接受客户端的数据,然后发送数据
void udp_server_recviveandsend(void)
{/*参数udp_socket为 建立udp服务端的*/recvfrom(udp_socket, rxBuf, rxbuf_len,0,(struct sockaddr *)&sockClient, &(sizeof(structsockaddr_in)));
sendto(udp_socket , txBuf, txbuf_len,0, (struct sockaddr *)&sockClient,sizeof(structsockaddr_in));
}//tcp客户端发数与接受
void udp_server_recviveandsend(void)
{/*tcpSock为建立*/send(tcpSock , txbuf, len,0);
recv(tcpSock , rxbuf, len,0);
}//tcp服务端是最难的,需要考虑客户端的ip,个数限制,并根据每个客户端进行通信,这里需要设置一个结构体数组,
typedefstructtcp_accept_t
{int lifeNum;//用来设置socket存在时间
int socket;//ulong ip; //保存客户端ip
ushort port;//保存客户端端口号
intlen;
}tcp_accept_t;#define num 100//定义最大接受客户端的数量,tcp_accept_t tcp_accept[num];
tcp接受客户端大体思路是这样想的:
1,利用select先获取accept之前的套接字
2,当accept响应,建立新套接字
3,有了新套接字,利用select获取receive的响应
4,如果新来客户端总数超过num,则放弃最早的客户端
create_tcpServer();structsockaddr_in sockClient;while(1)
{/*先*/
int socketMax=-1;
fd_set fdSockSet;intfd_act;structtimeval timeout;
FD_ZERO(&fdSockSet);//tcp_sockServer与create_tcpServer()一致
if(tcp_sockServer>0)
{
FD_SET(tcp_sockServer,&fdSockSet);if(socketMax < tcp_sockServer) {socketMax =tcp_sockServer};
}for(int i = 0; i
{if(tcp_accept[i].socket>0)
{
FD_SET(tcp_accept[i].socket,&fdSockSet);if(socketMax
}
}if(socketMax > 0){
timeout.tv_sec= 5;
timeout.tv_usec= 0;
fd_act= 0;
fd_act= select(socketMax+1, &fdSockSet, NULL, NULL, &timeout);if(fd_act>0)
{for(int i = 0; i
{if(tcp_accept[i].socket>0)
{
FD_SET(tcp_accept[i].socket,&fdSockSet);if(FD_ISSET(tcp_accept[i].socket, &fdSockSet))
{
recv(tcp_accept[i].socket, rxbuf, SOCKET_TCP_RECV,0);
}
}
}if(FD_ISSET(tcp_sockServer, &fdSockSet))
{
newSocket= -1;
newSocket= accept(tcp_sockServer, (struct sockaddr *)&sockClient, (socklen_t *)&addrlen); //sockclient为连接的客户端信息,addrlen为sockaddr结构体长度。 if(newSocket>0){
ip = ntohl(pTCP->sockClient.sin_addr.s_addr);
port= ntohs(pTCP->sockClient.sin_port);
sLinger.l_onoff= 0;
ret= setsockopt(newSocket, SOL_SOCKET, SO_LINGER, &sLinger, sizeof(structlinger));
lifeNumMax++;if(lifeNumMax >= 100000000){
lifeNumMax= 1;for(i=0;i 0){
tcpAccept[i].lifeNum-= 100000000;
}
}
}/*IP相同,则不需要关闭任何客户端*/
for (i = 0; i < SOCKET_ACCEPT_MAX; i++) {if (tcpAccept[i].socket > 0 && tcpAccept[i].ip ==ip) {
j=i;gotoaccept_new_socket;
}
}/*新来设备,判断是否num不够,如果num够则新增acceptsocket*/
for (i = 0; i < SOCKET_ACCEPT_MAX; i++) {if (tcpAccept[i].socket==0) {
j=i;gotoaccept_new_socket;
}
}/*新来设备,判断是否num不够,如果num不够则新增acceptsocket并放弃最早的客户端*/j= 0;
lifeNumMin= tcpAccept[0].lifeNum;for (i = 1; i < SOCKET_ACCEPT_MAX; i++) {if(lifeNumMin >tcpAccept[i].lifeNum){
lifeNumMin=tcpAccept[i].lifeNum;
j=i;
}
}
accept_new_socket:if(tcpAccept[j].socket > 0){
close(tcpAccept[j].socket);
}
tcpAccept[j].socket=newSocket;
tcpAccept[j].ip=ip;
tcpAccept[j].port=port;
tcpAccept[j].status= 0xff; /*socket正常使用*/tcpAccept[j].lifeNum=lifeNumMax;
}
}
}
其中放弃最早的客户端采用了比较简单的算法
每个客户端在连接时给予一个lifeNum生命值,还有一个不断增加的计数器 lifeNumMax,当客户端增加到最大数量时,比较前几个客户端的生命值,把最小的除去,因为它连接的最久。生命值代码为
lifeNumMax++;if(lifeNumMax >= 100000000){
lifeNumMax= 1;for(i=0;i 0){
tcpAccept[i].lifeNum-= 100000000;
}
}
}
原文链接:https://blog.csdn.net/qq_32166779/article/details/88861648