go语言之进阶篇WriteString的使用

本文展示了一个使用Go语言中的WriteString函数写入文件的详细示例。通过循环和格式化字符串,该函数将一系列带有计数器的行写入到名为'demo.txt'的文件中,展示了如何在Go中高效地进行文件写入操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、WriteString的使用

示例:

package main

import (
	"fmt"
	"os"
)

func WriteFile(path string) {
	f, err := os.Create(path)
	if err != nil {
		fmt.Println("err = ", err)
		return
	}

	//使用完毕,需要关闭文件
	defer f.Close()

	var buf string

	for i := 0; i < 10; i++ {
		//"i = 1\n", 这个字符串存储在buf中
		buf = fmt.Sprintf("i = %d\n", i)
		//fmt.Println("buf = ", buf)

		n, err := f.WriteString(buf)
		if err != nil {
			fmt.Println("err = ", err)
		}
		fmt.Println("n = ", n)
	}
}

func main() {
	path := "./demo.txt"

	WriteFile(path)
}

执行结果:    生成一个demo.txt文件

i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9

  

转载于:https://www.cnblogs.com/nulige/p/10267342.html

<think>我们正在讨论DNS解析过程,现在用户想在DNS解析过程中使用socket编程,并通过Go语言实现示例。 注意:DNS协议通常使用UDP(有时使用TCP)在53端口上进行通信。我们将构建一个简单的DNS查询请求,并解析响应。 步骤: 1. 构建DNS请求报文(二进制格式) 2. 通过UDP socket发送到DNS服务器(如8.8.8.8:53) 3. 接收响应并解析 DNS报文格式(简化): - 头部(12字节):包括ID、标志、问题数、回答数等 - 问题部分:查询的域名(格式特殊,如www.example.com会被转换为3www7example3com0,每个标签前有长度字节) - 回答部分(在响应中):包含资源记录(RR),如A记录 注意:我们这里只实现一个简单的查询,解析A记录。 由于DNS报文是二进制格式,我们需要手动构造和解析。但为了简化,我们可以使用标准库中的`net`包,它提供了发送DNS请求的方法(如`net.Resolver`),但这里我们需要用socket自己实现。 我们将自己构建DNS请求报文,然后通过UDP发送,再解析返回的报文。 参考:DNS协议标准(RFC1035) 以下是一个简单的Go程序示例,它使用原始UDP socket发送DNS查询并解析响应。 注意:此示例仅用于演示,实际应用中应使用标准库或更完善的DNS库。 步骤: 1. 创建UDP socket 2. 构建DNS查询报文(这里我们查询A记录) 3. 发送到DNS服务器 4. 接收响应 5. 解析响应(这里我们只解析一个A记录,忽略其他部分) 由于DNS报文格式复杂,我们只实现最基本的功能。 我们将构建一个函数将域名转换为DNS格式(例如:将"www.example.com"转换为[]byte{3, 'w','w','w', 7, 'e','x','a','m','p','l','e', 3, 'c','o','m', 0}) 注意:DNS头部需要设置一些标志,我们设置一个标准查询,并期望递归查询。 头部格式(12字节): 0-1: ID(随机生成) 2-3: 标志(我们设为0x0100,表示标准查询,递归期望) 4-5: 问题数(设为1) 6-7: 回答数(0) 8-9: 授权记录数(0) 10-11: 额外记录数(0) 问题部分: 域名(格式如上) 类型(2字节,A记录为1) 类(2字节,通常为1,表示IN) 响应解析:跳过相同格式的域名(可能是指针形式,需要处理),然后读取类型、类、TTL、数据长度和IP地址。 由于时间有限,我们只处理响应中第一个A记录,并假设响应中问题部分之后就是回答部分,且域名使用指针(常见情况)。 指针:如果域名的前两个比特是11,则表示指针,指针占2字节(去掉前两个比特后14位表示偏移)。 这个示例将尽量简单,但请注意,完整的DNS解析器非常复杂,这里仅用于学习。 代码示例:</think>### 在DNS解析中使用Socket编程的Go语言实现 #### DNS协议基础 DNS协议使用**UDP/TCP端口53**进行通信,默认采用UDP协议(最大报文512字节),当响应超过512字节时自动切换TCP。DNS报文格式如下: ```mermaid graph TD A[DNS报文结构] --> B[头部 12字节] A --> C[问题部分] A --> D[回答部分] A --> E[授权部分] A --> F[附加部分] B --> B1[事务ID] B --> B2[标志位] B --> B3[问题计数] B --> B4[回答计数] C --> C1[查询域名] C --> C2[查询类型] C --> C3[查询类] ``` #### Go语言实现DNS查询(UDP Socket) ```go package main import ( "encoding/binary" "fmt" "net" ) func main() { domain := "example.com" dnsServer := "8.8.8.8:53" // Google公共DNS // 创建UDP连接 conn, err := net.Dial("udp", dnsServer) if err != nil { panic(err) } defer conn.Close() // 构建DNS请求报文 msg := buildDNSQuery(domain) // 发送DNS查询 _, err = conn.Write(msg) if err != nil { panic(err) } // 接收响应 response := make([]byte, 1024) n, err := conn.Read(response) if err != nil { panic(err) } // 解析DNS响应 parseDNSResponse(response[:n], domain) } // 构建DNS查询报文 func buildDNSQuery(domain string) []byte { // 头部设置 header := make([]byte, 12) binary.BigEndian.PutUint16(header[0:2], 0x1234) // 事务ID binary.BigEndian.PutUint16(header[2:4], 0x0100) // 标志:标准查询 binary.BigEndian.PutUint16(header[4:6], 1) // 问题数=1 // 其他计数为0 // 问题部分 question := encodeDomainName(domain) question = append(question, []byte{0x00, 0x01, 0x00, 0x01}...) // 类型A, 类IN return append(header, question...) } // 域名编码转换(www.example.com -> 3www7example3com0) func encodeDomainName(domain string) []byte { var result []byte parts := splitDomain(domain) for _, part := range parts { result = append(result, byte(len(part))) result = append(result, part...) } return append(result, 0) // 结束符 } // 解析DNS响应 func parseDNSResponse(response []byte, domain string) { // 跳过头部(12字节) offset := 12 // 跳过问题部分 for response[offset] != 0 { offset += int(response[offset]) + 1 } offset += 5 // 跳过类型和类 // 解析回答部分 for i := 0; i < int(binary.BigEndian.Uint16(response[6:8])); i++ { // 跳过域名指针(2字节) offset += 2 // 读取类型和类 rrType := binary.BigEndian.Uint16(response[offset:offset+2]) offset += 10 // 跳过类、TTL和数据长度 // 解析A记录 if rrType == 1 { // A记录类型 ip := net.IPv4( response[offset], response[offset+1], response[offset+2], response[offset+3], ) fmt.Printf("%s has address %s\n", domain, ip) return } offset += 4 // 移动到下一个记录 } fmt.Println("No A record found") } // 辅助函数:分割域名 func splitDomain(domain string) []string { var parts []string start := 0 for i, char := range domain { if char == '.' { parts = append(parts, domain[start:i]) start = i + 1 } } parts = append(parts, domain[start:]) return parts } ``` #### 关键实现说明 1. **报文构建**: - 头部包含事务ID、标志位和计数字段 - 问题部分使用**标签长度前缀**格式编码域名(如 `www.example.com` 编码为 `3www7example3com0`) - 查询类型设为 `0x0001` (A记录),查询类设为 `0x0001` (IN) 2. **响应解析**: - 跳过12字节头部 - 处理域名指针(如 `0xC00C` 表示偏移量) - 提取A记录时读取4字节IPv4地址 - 支持多记录响应(通过回答计数字段循环) 3. **协议细节**: ```go // DNS头部标志位示例 0x0100 = 00000001 00000000 // QR(0) + Opcode(0000) + AA(0) + TC(0) + RD(1) ``` #### 执行结果示例 ``` example.com has address 93.184.216.34 ``` #### 进阶实现建议 1. **支持TCP回退**: ```go if len(response) > 512 { // 使用TCP重新查询 conn, _ = net.Dial("tcp", dnsServer+":53") } ``` 2. **处理CNAME记录**: ```go if rrType == 5 { // CNAME类型 // 递归解析别名 } ``` 3. **错误处理**: - 检查响应中的RCODE(响应码) - 处理格式错误的响应包 > **注意事项**:实际应用中推荐使用标准库 `net.Resolver` 或第三方DNS库(如 `github.com/miekg/dns`),原始Socket实现主要用于理解协议底层原理[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值