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

在这里插入图片描述
我们接下来的任务就是 解析这些字符串,从中获取 当前是什么方法,什么请求,参数是什么?

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

处理一个简单的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 = "<HTML><HEAD><TITLE>Method Not Implemented\r\n"
	_, _ = conn.Write([]byte(buf))
	buf = "</TITLE></HEAD>\r\n"
	_, _ = conn.Write([]byte(buf))
	buf = "<BODY><P>HTTP request method not supported.\r\n"
	_, _ = conn.Write([]byte(buf))
	buf = "</BODY></HTML>\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)
	}
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值