经过一段时间的学习、积累,自己对于物联网、计算机网络、socket编程等相关的领域和知识有了新的理解,网络部分一直是重中之重。因此重新再学习了Linux下的socket,并结合了一个简单的实际例子再来学习client端与server端工作流程。
===========================================
文件描述符:实际上就是Linux内核给分配的“称谓”,而在我们的TCP通讯中,会有多个文件描述符需要处理。
比如:
listenfd:监听描述符
connectfd:请求连接描述符
accept:接受连接描述符
read/write/recv/send…:IO描述符
服务器建立连接的流程和涉及到的函数:socket()、bind()、listen()、accept()、connect()、close()。
结构体struct sockaddr_in :网络通讯五元组,本端IP,本端端口、对端IP、对端端口、协议类型。
参考这张图便能了解client和server之间之间是如何进行交互的了。
===========================================
int socket(int domain, int type, int protocol);
向内核申请一个套接字,设置该套接字协议类型。
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
为套接字绑定IP和端口
int listen(int sockfd, int backlog);
以socket套接字和该套接字绑定的IP信息listen在内核开启监听,并返回监听描述符。这里代码是不阻塞的,但是内核一直在监听对应的端口。
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
连接请求,此时代码是阻塞的。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
接受连接的请求,代码阻塞。就是从listen中取描述符。
int close(int fd);
关闭描述符。
是不是就是类似我们熟悉的三次握手呀。
Client请求时间是不确定的,当多个请求到Server时,处于请求队列,等待listen的端口逐个处理至就绪队列。
connect处于阻塞态等待请求从listen的就绪队列被accept调度返回具体用于数据传输的accept_fd描述符。
accept处于阻塞态,当请求队列为空或处理完毕时。
所以,三次握手由connet发起,accept结束,途中经历listen的队列维护。
下面贴出代码,功能是由在开发板上的DS18B20采集了温度数据,上报到服务器,并且显示当前的时间。当然,前提是开发板已经使能了DS18B20的驱动,否则是跑不起应用程序的。
DS18B20的驱动编写可以参考这里:http://blog.youkuaiyun.com/u010944778/article/details/48058433
temp_server.c:
/*********************************************************************************
* Copyright: (C) 2017 TangBin<tangbinmvp@gmail.com>
* All rights reserved.
*
* Filename: temp_server.c
* Description: This file
*
* Version: 1.0.0(06/18/2017)
* Author: TangBin <tangbinmvp@gmail.com>
* ChangeLog: 1, Release initial version on "06/18/2017 08:22:56 PM"
*
********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <time.h>
#define MAXLINE 1024
#define PORT 8200
#define BUFFSIZE 2048
int main (int argc, char **argv)
{
int listenfd, connfd;
int n;
char buf[BUFFSIZE];
struct sockaddr_in servaddr;
if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("create socket error:");
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(PORT);
if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
{
perror("bind error:");
exit(0);
}
if(listen(listenfd, 10) < 0)
{
perror("listen error:");
exit(0);
}
printf("waiting client's request... ...\n");
while(1)
{
memset(&buf, 0, sizeof(buf));
time_t rawtime;
struct tm* ptime;
char time_arr[100];
time(&rawtime);
ptime = localtime(&rawtime);
strftime(time_arr, 20, "%y-%m-%d %I:%M:%S", ptime);
if((connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) < 0)
{
perror("accept error:");
continue;
}
n = recv(connfd, buf, MAXLINE, 0);
strcat(buf, "℃\0");
printf("temperature :%s\n", buf);
printf("%s\n",time_arr);
}
close(connfd);
return 0;
} /* ----- End of main() ----- */
temp_client.c:
/*********************************************************************************
* Copyright: (C) 2017 TangBin<tangbinmvp@gmail.com>
* All rights reserved.
*
* Filename: temp_client.c
* Description: This file
*
* Version: 1.0.0(06/19/2017)
* Author: TangBin <tangbinmvp@gmail.com>
* ChangeLog: 1, Release initial version on "06/19/2017 12:52:25 PM"
*
********************************************************************************/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define MAXLINE 1024
#define PORT 8200
float get_temp(void)
{
int fd;
int data = 0;
float temperature = 0;
unsigned char buff[2];
if((fd=open("/dev/ds18b20",O_RDWR | O_NDELAY | O_NOCTTY)) < 0)
{
perror("open device ds18b20 failed.");
exit(1);
}
printf("open ds18b20 success.");
read(fd, buff, sizeof(buff));
data = ((int)buff[1]) << 8; //高8位移动到16BITS的高8位
data |= (int)buff[0]; //合并低8位到温度读值
temperature = ((float)data)*0.0625;
/*DS18B20的温度操作是使用16位,也就是说分辨率是0.0625;要求出正数的十进制值,必须将读取到的LSB字节,MSB字节进行整合处理,然后乘以0.0625即可*/
close(fd);
printf("emperature is %4f \r\n",temperature);
return temperature;
}
int main (int argc, char **argv)
{
float res;
int socketfd;
char temp[50];
char s_line[MAXLINE];
struct sockaddr_in servaddr;
if(2 != argc)
{
printf("please input: ./client <ipaddr>\n");
return 0;
}
if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("create socket error: %s(errno: %d).\n", strerror(errno), errno);
return 0;
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) //点分十进制ip转换为点分二进制ip
{
printf("inet_pton error for %s.\n", argv[1]);
return 0;
}
if(connect(socketfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
{
printf("connect error: %s(errno: %d)\n", strerror(errno), errno);
return 0;
}
res = get_temp();
gcvt(res ,4 ,temp); //浮点型数转换为字符串,参数1:被转换的值;参数2:存储的有效数字位数;参数3:结果的存储位置。
memset(s_line ,0, sizeof(s_line));
strcpy(s_line, temp);
if(send(socketfd, s_line, strlen(s_line), 0) < 0) //将数据发送到服务器端
{
printf("send error:%s(errno: %d)\n", strerror(errno), errno);
return 0;
}
close(socketfd);
return 0;
} /* ----- End of main() ----- */
测试结果,用的是实验室的服务器和自己的开发板fl2440:
(其中connect error是因为在服务器上我的server程序没运行)