golang从0到1利用socket编程实现一个简单的http服务器

前置知识

https://blog.csdn.net/zhizhengguan/article/details/108026066

开始编程

第一份代码

package main

import (
	"fmt"
	"net"
)

func accept_request_thread(conn net.Conn)  {
     
	defer conn.Close()
	for {
     
		// 创建一个新切片, 用作保存数据的缓冲区
		buf := make([]byte, 1024)
		n, err := conn.Read(buf) // 从conn中读取客户端发送的数据内容
		if err != nil {
     
			fmt.Printf("客户端退出 err=%v\n", err)
			return
		}


		fmt.Printf(" 接受消息 %s\n", string(buf[:n]))
	}
}

func main() {
     
	listen, err := net.Listen("tcp", ":8888")  // 创建用于监听的 socket
	if err != nil {
     
		fmt.Println("listen err=", err)
		return
	}
	fmt.Println("监听套接字,创建成功, 服务器开始监听。。。")
	defer listen.Close()  // 服务器结束前关闭 listener

	// 循环等待客户端来链接
	for   {
     
		fmt.Println("阻塞等待客户端来链接...")
		conn, err := listen.Accept() // 创建用户数据通信的socket
		if err != nil {
     
			fmt.Println("Accept() err=", err)
		} else {
     
			fmt.Println("通信套接字,创建成功。。。")
		}
		// 这里准备起一个协程,为客户端服务
		go accept_request_thread(conn)
	}
}
  • 浏览器发送一个get请求: http://192.168.0.20:8888/api/camera/get_ptz?camera_id=1324566666789876543
  • 服务端接受到的消息如下:
http://192.168.0.20:8888/api/camera/get_ptz?camera_id=1324566666789876543

golang从0到1利用socket编程实现一个简单的http服务器_第1张图片
我们接下来的任务就是 解析这些字符串,从中获取 当前是什么方法,什么请求,参数是什么?

先定义一个小目标,获取当前是什么方法。

处理一个简单的get请求

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"strings"
)

func unimplemented(conn net.Conn){
     
	var buf string

	buf = "HTTP/1.0 501 Method Not Implemented\r\n"
	_, _ = conn.Write([]byte(buf))
	buf = "Server: httpd/0.1.0\r\n"
	_, _ = conn.Write([]byte(buf))
	buf = "Content-Type: text/html\r\n"
	_, _ = conn.Write([]byte(buf))
	buf = "\r\n"
	_, _ = conn.Write([]byte(buf))
	buf = "Method Not Implemented\r\n"</span>
	<span class="token boolean">_</span><span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">=</span> conn<span class="token punctuation">.</span><span class="token function">Write</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token function">byte</span><span class="token punctuation">(</span>buf<span class="token punctuation">)</span><span class="token punctuation">)</span>
	buf <span class="token operator">=</span> <span class="token string">"\r\n"
	_, _ = conn.Write([]byte(buf))
	buf = "

HTTP request method not supported.\r\n" _, _ = conn.Write([]byte(buf)) buf = "\r\n" _, _ = conn.Write([]byte(buf)) } func accept_request_thread(conn net.Conn) { defer conn.Close() var i int buf := make([]byte, 1024) n, err := conn.Read(buf) // 从conn中读取客户端发送的数据内容 if err != nil { fmt.Printf("客户端退出 err=%v\n", err) return } // 获取方法 i = 0 var method_bt strings.Builder for(i < n && buf[i] != ' '){ method_bt.WriteByte(buf[i]) i++; } method := method_bt.String() if(method != "GET"){ unimplemented(conn) return } for(i < n && buf[i] == ' '){ i++ } //api/camera/get_ptz?camera_id=1324566666789876543 var url_bt strings.Builder for(i < n && buf[i] != ' '){ url_bt.WriteByte(buf[i]) i++; } url := url_bt.String() if(method == "GET"){ //url ---> /api/camera/get_ptz?camera_id=1324566666789876543 // 跳到第一个? var path, query_string string j := strings.IndexAny(url, "?") if(j != -1){ path = url[:j] if(j + 1 < len(url)){ query_string = url[j+1:] } }else{ path = url } fmt.Print(path + "请求已经创建\t") resp := execute(path, query_string)// =1324566666789876543 fmt.Println("返回", string(resp)) header(conn, "application/json", len(resp)); _ , err := conn.Write(resp) if(err != nil){ fmt.Println(err) } } } //回应客户端必须先设置好head头,浏览器才能解析 func header(conn net.Conn, content_type string , length int ) { var buf string buf = "HTTP/1.0 200 OK\r\n" _, _ = conn.Write([]byte(buf)) buf = "Server: httpd/0.1.0\r\n" _, _ = conn.Write([]byte(buf)) buf = "Content-Type: " + content_type + "\r\n" _, _ = conn.Write([]byte(buf)) _, _ = fmt.Sscanf(buf, "Content-Length: %d\r\n", length) buf = "Content-Type: " + content_type + "\r\n" _, _ = conn.Write([]byte(buf)) buf = "\r\n" _, _ = conn.Write([]byte(buf)) } func execute(path string, query_string string) ([]byte) { query_params := make(map[string]string) parse_query_params(query_string, query_params) if("/api/camera/get_ptz" == path){ /* * do something */ camera_id := query_params["camera_id"] resp := make(map[string]interface{ }) resp["camera_id"] = camera_id resp["code"] = 200 resp["msg"] = "ok" rs, err := json.Marshal(resp) if err != nil{ log.Fatalln(err) } return rs }else if("get_abc" == path){ /* * do something */ return []byte("abcdcvfdswa") } return []byte("do't match") } /*map作为函数入参是作为指针进行传递的 函数里面对map进行修改时,会同时修改源map的值,但是将map修改为nil时,则达不到预期效果。*/ // camera_id=1324566666789876543&tt=%E5%88%9B%E5%BB%BA%E6%88%90%E5%8A%9F func parse_query_params(query_string string, query_params map[string]string) { kvs := strings.Split(query_string, "&") if(len(kvs) == 0){ return } for _, kv := range kvs { kv := strings.Split(kv, "=") if(len(kv) != 2){ continue } query_params[kv[0]] = kv[1] } } func main() { listen, err := net.Listen("tcp", ":8888") // 创建用于监听的 socket if err != nil { fmt.Println("listen err=", err) return } fmt.Println("监听套接字,创建成功, 服务器开始监听。。。") defer listen.Close() // 服务器结束前关闭 listener // 循环等待客户端链接 for { fmt.Println("阻塞等待客户端链接...") conn, err := listen.Accept() // 创建用户数据通信的socket if err != nil { panic("Accept() err= " + err.Error()) } // 这里准备起一个协程,为客户端服务 go accept_request_thread(conn) } }

你可能感兴趣的:(#,golang)