一、socket编程实现服务器与客户端连接
1.了解TCP/IP协议和服务器和客户端的通信原理
TCP/IP协议本人还不太懂,有需要者请查看
太棒了!TCP/IP协议 (图解+秒懂+史上最全)这位博主的文章,
总的来说一张图概括:
2.创建服务器和客户端
1)创建服务器
从上图可知,创建服务器所要用到的函数为
socket()
创建套接字文字描述法server_fd
bind()
把文字描述符与端口PORT绑定起来
listen()监听
accept()
阻塞,一直有客户端连接才会连通
read()/write()
需要注意的是服务器写,客户端就读
再者,read()和 write()函数的使用方法需要仔细查看,本人在使用读写过程中就犯了许多错误
write()/read()
close()关闭文字描述法
具体代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <sys/stat.h>
#define PORT 8080
#define TEST_TXT "11111"
#define TEMPERATURE "Temperature is %.2f"
int Get_DS18B20_Temp(float *temp);
int main(int argc,char **agrv)
{
int server_fd,client_fd;
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
socklen_t cliaddr_len;
int addrlen = sizeof(serv_addr);
char buf[1024];
float temp;
int client_rv;
char message[1024];
//创建 socket 文件描述符
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if(server_fd < 0)
{
printf("Create socket failure: %s\n", strerror(errno));
return -1;
}
printf("Create socket fd[%d]\n", server_fd);
// 设置 socket 选项, 允许多个连接相同的端口
/*if(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)))
{
printf("Set more port failure: %s\n", strerror(errno));
goto cleanup;
}*/
memset(&serv_addr, 0 ,sizeof(serv_addr)); //初始化serv_addr 的值
serv_addr.sin_family = AF_INET; //ipv4
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(PORT); //htons():将服务器ip转为客户端ip
//绑定 socket 到指定的地址和端口
if (bind(server_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("Create socket failure: %s\n", strerror(errno));
return -2;
}
printf("Create[%d] bind on port[%d] for all IP address OK\n", server_fd, PORT);
//设置监听,连接
listen(server_fd, 3);
//等待客户端连接
while(1)
{
printf("\nStar waiting and accept new client connect ...\n", server_fd);
client_fd = accept(server_fd, (struct sockaddr*)&cli_addr, &cliaddr_len);
if(client_fd < 0)
{
printf("create accept failure: %s\n", strerror(errno));
return -2;
}
printf("Accept new client[%s:%d] with fd [%d]\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), client_fd);
//调用Get_DS18B20_Temp函数读取温度
if((Get_DS18B20_Temp(&temp)) < 0)
{
printf("ds18b20 get temperature failure\n");
return -3;
}
printf("Temperature is:%.3f C\n", temp);
//测试用例
/*if((write(client_fd, TEST_TXT, strlen(TEST_TXT))) < 0)
{
printf("write faile: %s\n", strerror(errno));
goto cleanup;
}
printf("write ok");*/
//将temp的值转换为字符串
int len = snprintf(buf, sizeof(buf), TEMPERATURE, temp);
if(len < 0 || len >= sizeof(buf))
{
fprintf(stderr,"error converting temp to string\n");
exit(0);
}
printf("%s\n", buf);
//读取文件内容并发送给客户端
if(write(client_fd, buf, sizeof(buf)) < 0)
{
printf("write data to client [%d] failure: %s\n", PORT, strerror(errno));
goto cleanup;
}
/*snprintf(message, sizeof(message), "%d", temp);
while(temp > 0)
{
send(client_fd, buf, strlen(message), 0);
memset(buf, 0, sizeof(buf));
}*/
//读取client端返回的数据
memset(buf, 0, sizeof(buf));
if((client_rv = read(client_fd, buf, sizeof(buf))) < 0)
{
printf("read data from client failure: %s\n", strerror(errno));
goto cleanup;
}
else if(client_rv == 0)
{
printf("server connect to client get dis\n");
goto cleanup;
}
printf("read %d bytes data from client: %s\n", client_rv, buf);
sleep(1);
close(client_fd);
}
cleanup:
close(server_fd);
return 0;
}
int Get_DS18B20_Temp(float *temp)
{
int fd = -1;
int rv = -1;
char path[50] = "/sys/bus/w1/devices/";
char dir_name[20];
DIR *dirp;
int found = -1;
struct dirent *direntp;
char buf[128];
char *ptr;
memset(dir_name, 0, sizeof(dir_name));
memset(buf, 0, sizeof(buf));
if((dirp = opendir(path)) == NULL)
{
printf("open dir '%s' failure: %s\n", path, strerror(errno));
return -1;
}
printf("open dir '%s' successfully\n", path);
while((direntp = readdir(dirp)) != NULL)
{
if(strstr(direntp->d_name, "28-") == NULL)
continue;
strncpy(dir_name, direntp->d_name,strlen(direntp->d_name));
printf("Find file:%s\n", dir_name);
found = 1;
break;
}
closedir(dirp);
if(found == -1)
{
printf("can not find ds18b20 in '%s'\n", path);
return -2;
}
strncat(path, dir_name, strlen(dir_name));
strncat(path,"/w1_slave", sizeof(path)-strlen(path));
if((fd = open(path, O_RDONLY)) < 0)
{
printf("open file '%s'\n", path, strerror(errno));
return -3;
}
printf("open file '%s' fd[%d] successfully\n", path, fd);
if((rv = read(fd, buf, sizeof(buf))) < 0)
{
printf("read data from file '%s' failure: %s\n", path, strerror(errno));
rv = -4;
goto cleanup;
}
printf("read %dB data from file '%s'\n", rv, path);
if((ptr = strstr(buf, "t=") + 2) == NULL)
{
printf("find data failure: %s\n", strerror(errno));
rv = -5;
goto cleanup;
}
*temp = atof(ptr)/1000;
cleanup:
if(fd > 0)
close(fd);
return rv;
}
2)创建客户端
创建客户端流程为:
- socket()
- connect()创建连接
- write()/read()
- read()/write()
- close()
客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 8080
#define SERVER_IP "127.0.0.1"
int main(int argc, char **argv)
{
int conn_fd, value_read;
struct sockaddr_in serv_addr;
char buf[1024];
int rv = -1;
double temp;
//创建 connet 的文件描述符
if((conn_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("create conn_fd failure: %s\n", strerror(errno));
return -1;
}
printf("Create conn_fd ok\n");
//将ipv4地址从点分十进制转换为二进制
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
inet_aton( SERVER_IP, &serv_addr.sin_addr);
if((connect(conn_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) < 0)
{
printf("create connect failure: %s\n", strerror(errno));
return -2;
}
printf("\nCreate connect OK\n");
memset(buf, 0, sizeof(buf));
if((rv = read(conn_fd, buf, sizeof(buf))) < 0)
{
printf("read data from server socket[%d] failure: %s\n", conn_fd, strerror(errno));
close(conn_fd);
}
else if(rv == 0)
{
printf("server socket[%d] disconnected\n", conn_fd);
close(conn_fd);
}
printf("read %d bytes data from server[%d] and echo it back: '%s'\n", rv, conn_fd, buf);
if((write(conn_fd, buf, rv)) < 0)
{
printf("write %d bytes data back to server[%d] failure: %s\n", rv, conn_fd, strerror(errno));
close(conn_fd);
}
/*while((value_read = read(conn_fd, buf, sizeof(buf))) > 0)
{
printf("%s", buf);
memset(buf, 0, sizeof(buf));
}*/
close(conn_fd);
return 0;
}
需要注意的是,int Get_DS18B20_Temp函数传回的是float类型的变量,需要使用snprintf()函数将传回来的temp变量转换为char字符类型进行传输,并以二进制类型存在buf缓存空间内。