15.1 TCP 服务器
服务器:
package main
import (
"fmt"
"net"
)
func main() {
fmt.Println("Starting the server ...")
// 创建 listener
listener, err := net.Listen("tcp", "localhost:50000")
if err != nil {
fmt.Println("Error listening", err.Error())
return // 终止程序
}
// 监听并接受来自客户端的连接
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting", err.Error())
return // 终止程序
}
go doServerStuff(conn)
}
}
func doServerStuff(conn net.Conn) {
for {
buf := make([]byte, 512)
len, err := conn.Read(buf)
if err != nil {
fmt.Println("Error reading", err.Error())
return // 终止程序
}
fmt.Printf("Received data: %v", string(buf[:len]))
}
}
客户端:
package main
import (
"bufio"
"fmt"
"net"
"os"
"strings"
)
func main() {
// 打开连接:
conn, err := net.Dial("tcp", "localhost:50000")
if err != nil {
// 由于目标计算机积极拒绝而无法创建连接
fmt.Println("Error dialing", err.Error())
return // 终止程序
}
inputReader := bufio.NewReader(os.Stdin)
fmt.Println("First, what is your name?")
clientName, _ := inputReader.ReadString('\n')
// fmt.Printf("CLIENTNAME %s", clientName)
trimmedClient := strings.Trim (clientName, "\r\n") // Windows 平台下用 "\r\n",Linux 平台下使用 "\n"
// 给服务器发送信息直到程序退出:
for {
fmt.Println("What to send to the server? Type Q to quit.")
input, _ := inputReader.ReadString('\n')
trimmedInput := strings.Trim(input, "\r\n")
// fmt.Printf("input:--%s--", input)
// fmt.Printf("trimmedInput:--%s--", trimmedInput)
if trimmedInput == "Q" {
return
}
_, err = conn.Write([]byte(trimmedClient + " says: " + trimmedInput))
}
}
此处的服务器使用协程来处理多个用户的连接。
15.2 一个简单的 Web 服务器
package main
import (
"fmt"
"log"
"net/http"
)
func HelloServer(w http.ResponseWriter, req *http.Request) {
fmt.Println("Inside HelloServer handler")
fmt.Fprintf(w, "Hello,"+req.URL.Path[1:])
}
func main() {
http.HandleFunc("/", HelloServer)
err := http.ListenAndServe("localhost:8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err.Error())
}
}
15.3 一个简单的 Web 客户端
package main
import (
"fmt"
"net/http"
)
var urls = []string{
"http://www.google.com/",
"http://golang.org/",
"http://blog.golang.org/",
}
func main() {
// Execute an HTTP HEAD request for all url's
// and returns the HTTP status string or an error string.
for _, url := range urls {
resp, err := http.Head(url)
if err != nil {
fmt.Println("Error:", url, err)
}
fmt.Println(url, ": ", resp.Status)
}
}
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
)
func main() {
res, err := http.Get("http://www.google.com")
checkError(err)
data, err := ioutil.ReadAll(res.Body)
checkError(err)
fmt.Printf("Got: %q", string(data))
}
func checkError(err error) {
if err != nil {
log.Fatalf("Get : %v", err)
}
}
15.4 模板
package main
import (
"net/http"
"io/ioutil"
"log"
"regexp"
"text/template"
)
const lenPath = len("/view/")
var titleValidator = regexp.MustCompile("^[a-zA-Z0-9]+$")
var templates = make(map[string]*template.Template)
var err error
type Page struct {
Title string
Body []byte
}
func init() {
for _, tmpl := range []string{"edit", "view"} {
templates[tmpl] = template.Must(template.ParseFiles(tmpl + ".html"))
}
}
func main() {
http.HandleFunc("/view/", makeHandler(viewHandler))
http.HandleFunc("/edit/", makeHandler(editHandler))
http.HandleFunc("/save/", makeHandler(saveHandler))
err := http.ListenAndServe("localhost:8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err.Error())
}
}
func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[lenPath:]
if !titleValidator.MatchString(title) {
http.NotFound(w, r)
return
}
fn(w, r, title)
}
}
func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
p, err := load(title)
if err != nil { // page not found
http.Redirect(w, r, "/edit/"+title, http.StatusFound)
return
}
renderTemplate(w, "view", p)
}
func editHandler(w http.ResponseWriter, r *http.Request, title string) {
p, err := load(title)
if err != nil {
p = &Page{Title: title}
}
renderTemplate(w, "edit", p)
}
func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
body := r.FormValue("body")
p := &Page{Title: title, Body: []byte(body)}
err := p.save()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/view/"+title, http.StatusFound)
}
func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
err := templates[tmpl].Execute(w, p)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (p *Page) save() error {
filename := p.Title + ".txt"
// file created with read-write permissions for the current user only
return ioutil.WriteFile(filename, p.Body, 0600)
}
func load(title string) (*Page, error) {
filename := title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return &Page{Title: title, Body: body}, nil
}