Linux C 实现HTTP的增删改查(GET POST PUT DELETE)
全部代码
//http.h文件
#ifndef _MY_HTTP_H
#define _MY_HTTP_H
#define MY_HTTP_DEFAULT_PORT 80
char * http_get(const char *url);
char * http_post(const char *url,const char * post_str);
char * http_delete(const char *url);
char * http_put(const char *url,const char * post_str);
#endif
//http.c文件
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include "http.h"
#define BUFFER_SIZE 1024
#define HTTP_POST "POST /%s HTTP/1.1\r\nHOST: %s:%d\r\nAccept: */*\r\nContent-Type:application/json\r\nContent-Length: %d\r\n\r\n%s"
#define HTTP_GET "GET /%s HTTP/1.1\r\nHOST: %s:%d\r\nAccept: */*\r\n\r\n"
#define HTTP_PUT "PUT /%s HTTP/1.1\r\nHOST: %s:%d\r\nAccept: */*\r\nContent-Type:application/json\r\nContent-Length: %d\r\n\r\n%s"
#define HTTP_DELETE "DELETE /%s HTTP/1.1\r\nHOST: %s:%d\r\nAccept: */*\r\n\r\n"
static int http_tcpclient_create(const char *host, int port){
struct hostent *he;
struct sockaddr_in server_addr;
int socket_fd;
if((he = gethostbyname(host))==NULL){
return -1;
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr = *((struct in_addr *)he->h_addr);
if((socket_fd = socket(AF_INET,SOCK_STREAM,0))==-1){
return -1;
}
if(connect(socket_fd, (struct sockaddr *)&server_addr,sizeof(struct sockaddr)) == -1){
return -1;
}
return socket_fd;
}
static void http_tcpclient_close(int socket){
close(socket);
}
static int http_parse_url(const char *url,char *host,char *file,int *port) //分析url
{
char *ptr1,*ptr2;
int len = 0;
if(!url || !host || !file || !port){
return -1;
}
ptr1 = (char *)url; //如果是"http://127.0.0.1:8080/1213
if(!strncmp(ptr1,"http://",strlen("http://"))){
ptr1 += strlen("http://"); //移动指针到 "http://"的后面 (127.0.0.1:8080/1213)
}else{
return -1;
}
ptr2 = strchr(ptr1,'/'); //(/1213)
if(ptr2){
len = strlen(ptr1) - strlen(ptr2);
memcpy(host,ptr1,len);
host[len] = '\0'; //(127.0.0.1:8080)
if(*(ptr2 + 1)){ //移动指针 (1213)
memcpy(file,ptr2 + 1,strlen(ptr2) - 1 );
file[strlen(ptr2) - 1] = '\0';
}
}else{
memcpy(host,ptr1,strlen(ptr1));
host[strlen(ptr1)] = '\0';
}
//get host and ip
ptr1 = strchr(host,':'); //(8080)
if(ptr1){
*ptr1++ = '\0';
*port = atoi(ptr1);
}else{
*port = MY_HTTP_DEFAULT_PORT;
}
return 0;
}
static int http_tcpclient_recv(int socket,char *lpbuff){ //不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据
int recvnum = 0;
recvnum = recv(socket, lpbuff,BUFFER_SIZE*4,0); //lpbuff(指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据)
lpbuff[recvnum] = 0; //这里需要加个终止符,因为lpbuff是每一次请求中至始至终使用的缓冲区,没有加终止符的话,会导致接收到的数据残留一些上一次接收的
return recvnum;
}
static int http_tcpclient_send(int socket,char *buff,int size){ //发送数据
int sent=0,tmpres=0;
while(sent < size){
tmpres = send(socket,buff+sent,size-sent,0);
if(tmpres == -1){
return -1;
}
sent += tmpres;
}
return sent;
}
static char *http_parse_result(const char*lpbuf){ //处理响应报文
char *ptmp = NULL;
char *response = NULL;
ptmp = (char*)strstr(lpbuf,"HTTP/1.1");
if(!ptmp){
printf("http/1.1 not faind\n");
return NULL;
}
if(atoi(ptmp + 9)!=200){ //atoi把字符串转换为整数 //移动ptmp的指针,得到状态码,状态码为200的话表示成功
printf("result:\n%s\n",lpbuf);
return NULL;
}
ptmp = (char*)strstr(lpbuf,"\r\n\r\n");
if(!ptmp){
printf("ptmp is NULL\n");
return NULL;
}
response = (char *)malloc(strlen(ptmp)+1); //记住malloc出来的堆空间要free,一次malloc一次free,否则会内存泄露,free可以在调用的函数那里再free
if(!response){
printf("malloc failed \n");
return NULL;
}
strcpy(response,ptmp+4);
return response;
}
char * http_post(const char *url,const char *post_str){ //处理post请求
char post[BUFFER_SIZE] = {'\0'};
int socket_fd = -1;
char lpbuf[BUFFER_SIZE*4] = {'\0'};
char *ptmp;
char host_addr[BUFFER_SIZE] = {'\0'};
char file[BUFFER_SIZE] = {'\0'};
int port = 0;
int len=0;
char *response = NULL;
if(!url || !post_str){
printf(" failed!\n");
return NULL;
}
if(http_parse_url(url,host_addr,file,&port)<0){
printf("http_parse_url failed!\n");
return NULL;
}
// printf("host_addr : %s\tfile:%s\t,%d\n",host_addr,file,port);
socket_fd = http_tcpclient_create(host_addr,port);
if(socket_fd < 0){
printf("http_tcpclient_create failed\n");
return NULL;
}
sprintf(lpbuf,HTTP_POST,file,host_addr,port,strlen(post_str),post_str);
if(http_tcpclient_send(socket_fd,lpbuf,strlen(lpbuf)) < 0){
printf("http_tcpclient_send failed..\n");
http_tcpclient_close(socket_fd); //当文件描述符创建成功后,都要close,不然文件描述符有可能会用尽
return NULL;
}
//printf("发送请求:\n%s\n",lpbuf);
/*it's time to recv from server*/
if(http_tcpclient_recv(socket_fd,lpbuf) <= 0){
printf("http_tcpclient_recv failed\n");
http_tcpclient_close(socket_fd);
return NULL;
}
http_tcpclient_close(socket_fd);
return http_parse_result(lpbuf);
}
char * http_put(const char *url,const char *post_str){ //处理put请求
char post[BUFFER_SIZE] = {'\0'};
int socket_fd = -1;
char lpbuf[BUFFER_SIZE*4] = {'\0'};
char *ptmp;
char host_addr[BUFFER_SIZE] = {'\0'};
char file[BUFFER_SIZE] = {'\0'};
int port = 0;
int len=0;
char *response = NULL;
if(!url || !post_str){
printf(" failed!\n");
return NULL;
}
if(http_parse_url(url,host_addr,file,&port)<0){
printf("http_parse_url failed!\n");
return NULL;
}
// printf("host_addr : %s\tfile:%s\t,%d\n",host_addr,file,port);
socket_fd = http_tcpclient_create(host_addr,port);
if(socket_fd < 0){
printf("http_tcpclient_create failed\n");
return NULL;
}
sprintf(lpbuf,HTTP_PUT,file,host_addr,port,strlen(post_str),post_str);
if(http_tcpclient_send(socket_fd,lpbuf,strlen(lpbuf)) < 0){
printf("http_tcpclient_send failed..\n");
return NULL;
}
//printf("发送请求:\n%s\n",lpbuf);
/*it's time to recv from server*/
if(http_tcpclient_recv(socket_fd,lpbuf) <= 0){
printf("http_tcpclient_recv failed\n");
return NULL;
}
http_tcpclient_close(socket_fd);
return http_parse_result(lpbuf);
}
char * http_get(const char *url) //处理get请求
{
char post[BUFFER_SIZE] = {'\0'};
int socket_fd = -1;
char lpbuf[BUFFER_SIZE*4] = {'\0'};
char *ptmp;
char host_addr[BUFFER_SIZE] = {'\0'};
char file[BUFFER_SIZE] = {'\0'};
int port = 0;
int len=0;
if(!url){
printf(" failed!\n");
return NULL;
}
if(http_parse_url(url,host_addr,file,&port)<0){
printf("http_parse_url failed!\n");
return NULL;
}
//printf("host_addr : %s\tfile:%s\t,%d\n",host_addr,file,port);
socket_fd = http_tcpclient_create(host_addr,port);
if(socket_fd < 0){
printf("http_tcpclient_create failed\n");
return NULL;
}
sprintf(lpbuf,HTTP_GET,file,host_addr,port);
if(http_tcpclient_send(socket_fd,lpbuf,strlen(lpbuf)) < 0){
printf("http_tcpclient_send failed..\n");
return NULL;
}
// printf("发送请求:\n%s\n",lpbuf);
if(http_tcpclient_recv(socket_fd,lpbuf) <= 0){
printf("http_tcpclient_recv failed\n");
return NULL;
}
http_tcpclient_close(socket_fd);
return http_parse_result(lpbuf);
}
char * http_delete(const char *url) //处理delete请求
{
char post[BUFFER_SIZE] = {'\0'};
int socket_fd = -1;
char lpbuf[BUFFER_SIZE*4] = {'\0'};
char *ptmp;
char host_addr[BUFFER_SIZE] = {'\0'};
char file[BUFFER_SIZE] = {'\0'};
int port = 0;
int len=0;
if(!url){
printf(" failed!\n");
return NULL;
}
if(http_parse_url(url,host_addr,file,&port)<0){
printf("http_parse_url failed!\n");
return NULL;
}
//printf("host_addr : %s\tfile:%s\t,%d\n",host_addr,file,port);
socket_fd = http_tcpclient_create(host_addr,port);
if(socket_fd < 0){
printf("http_tcpclient_create failed\n");
return NULL;
}
sprintf(lpbuf,HTTP_DELETE,file,host_addr,port);
if(http_tcpclient_send(socket_fd,lpbuf,strlen(lpbuf)) < 0){
printf("http_tcpclient_send failed..\n");
return NULL;
}
// printf("发送请求:\n%s\n",lpbuf);
if(http_tcpclient_recv(socket_fd,lpbuf) <= 0){
printf("http_tcpclient_recv failed\n");
return NULL;
}
http_tcpclient_close(socket_fd);
return http_parse_result(lpbuf);
}
int main() //用了自己的项目做测试
{
char * result = NULL;
char * send = "{\"name\": \"老王\",\"getCash\": 0.0,\"aopenid\": \"sssssss\",\"aid\": 1,\"phone\": \"15914716715\"}";
//注意json文件定义成字符串时,引号前面要加转义符。而且这里要写出一行,不然也会出错。具体原因我也还不知道。
// result = http_get("http://192.168.1.101:18081/admin/1");
// result = http_delete("http://192.168.1.101:18081/admin/3");
// result = http_post("http://192.168.1.101:18081/admin",send);
result = http_put("http://192.168.1.101:18081/admin/1",send);
printf("%s",result);
return 0;
}
HTTP请求报文的格式
主要由请求行、请求头部、空行、请求数据组成。
请求行:由3部分组成,分别为:请求方法(GET、POST、PUT等)、URL以及协议版本(协议版本的格式为:HTTP/主版本号.次版本号,常用的有HTTP/1.0和HTTP/1.1),每两个之间由空格分隔。
请求头部:包含很多客户端环境以及请求正文的有用信息。请求头部由“关键字:值”对组成,每行一堆,关键字和值之间使用英文“:”分隔。
空行:注意这个是必不可少的,是固定格式。表示请求头部结束
请求正文:可选部分,比如GET请求就没有请求正文;POST比如以提交表单数据方式为请求正文。
如本代码的测试结果:
GET的请求报文
POST的请求报文
HTTP的响应报文格式
主要由状态行、响应头部、空行和响应正文组成。
状态行:由3部分组成,分别为:协议版本,状态码,状态码描述,每两个之间由空格分隔。状态代码为3位数字,200-299的状态码表示成功,300-399的状态码指资源重定向,400-499的状态码指客户端请求出错,500-599的状态码指服务端出错(HTTP/1.1向协议中引入了信息性状态码,范围为100-199)。
响应头部:与请求头部类似,也包含了很多有用的信息。
空行:这一行必不可少。表示响应头部结束。
响应正文:服务器返回的文档,最常见的为HTML网页。
不过本代码返回的是json文件。
POST的响应报文:
总结:四种请求需要的处理(socket的创建、请求报文的发送、url的分析、响应报文的接收、响应报文的处理以及socket的关闭)的代码都是一样的,只是各自请求内部的操作有所不同。
关于HTTP报文格式的可以参考:https://www.cnblogs.com/weibanggang/p/9454581.html
https://blog.csdn.net/weixin_39945445/article/details/112090690
有错误的地方还请斧正!
还有本文章是我参考网上的资料整理的,有所冒犯,还请作者联系我。