GO语言基础教程(232)Go网络编程之Socket编程:Go语言Socket编程:从青铜到王者的进阶之路

掌握Socket,让你的Go程序在网络世界畅行无阻

当Socket遇上Go:网络编程的终极进化

在C语言中搞Socket编程,就像在原始森林里徒步——你得小心翼翼绕过无数陷阱:手动管理连接、处理并发、防御各种错误...光记下那些API就够喝一壶了。但当你用Go写Socket程序时,瞬间有种鸟枪换炮的感觉:简洁的API、内置的并发、稳固的标准库,让你仿佛开着越野车穿越那片森林。

Socket本是网络通信的基石,它允许不同计算机上的进程相互通信。在UNIX哲学中,一切皆文件,Socket就是一种特殊的文件描述符。但Go的设计者们深刻理解开发者的痛,通过net包将复杂度封装得如此优雅,以至于你只需几行代码就能搭建强大的网络应用。

为什么Go特别适合网络编程?

想象一下,一个能够轻松处理10万并发连接的服务端程序,用传统语言可能需要高超的技术和复杂的架构,而用Go,甚至初级开发者都能在几天内实现。这得益于Go天生的并发模型和网络库设计。

Socket基础:三分钟理解网络通信核心

什么是Socket?

简单来说,Socket是网络通信的端点,就像现实世界中的电话插座:一端插入,另一端也插入,双方就能通话了。每个Socket绑定一个地址(IP+端口),遵循特定的网络协议(TCP或UDP)。

TCP与UDP的区别

特性

TCP

UDP

连接

面向连接

无连接

可靠性

高(确认和重传机制)

低(不保证数据传输)

速度

较慢

较快

数据顺序

保证顺序

无序

适用场景

文件传输、HTTP请求等

实时视频、在线游戏等

TCP像打电话,需要接通且保证对方听到每一句话;UDP像发短信,发送出去就不管了,可能丢失也可能乱序。

Go中的Socket抽象

Go语言通过net包提供了丰富的Socket编程接口。两个核心抽象是:

  • Listener:用于服务端,监听 incoming 连接
  • Conn:代表一个已建立的连接,可用于读写数据

这种抽象将开发者从繁琐的底层细节中解放出来。在C中需要几十行代码的功能,在Go中只需寥寥数行。

Go语言Socket编程实战:从入门到熟练

TCP服务端:让你的程序学会"接电话"

先来看一个完整的TCP服务端实现,它就像一部总机电话,等待客户端呼叫:

package main

import (
    "bufio"
    "fmt"
    "net"
    "strings"
)

func main() {
    // 1. 创建监听器 - 安装一部电话总机
    listener, err := net.Listen("tcp", "localhost:8080")
    if err != nil {
        fmt.Println("开启socket服务失败:", err)
        return
    }
    defer listener.Close() // 程序退出时关闭电话总机
    fmt.Println("服务器已启动,等待客户端连接...")
    
    for {
        // 2. 接受客户端连接 - 等待电话打入
        conn, e
以下是 **基于 Dev-C++ 和 Winsock 的完整联机算数游戏**,包括服务器端与客户端的全部功能。代码已经**全部提示改为中文**,功能完整,包括: - 用户注册与登录 - 控制台首页(不使用任何图形库) - 匹配机制(两两一组) - 房间创建与题目发送 - 答题计时 - 段位提升 --- ## ✅ 一、服务器端代码(server.cpp) ```cpp #include <iostream> #include <winsock2.h> #include <ws2tcpip.h> #include <vector> #include <thread> #include <mutex> #include <map> #include <queue> #include <ctime> #include <cstdlib> #include <sstream> #pragma comment(lib, "ws2_32.lib") #define PORT 8888 #define BUF_SIZE 1024 std::map<std::string, std::string> users; // 用户名 -> 密码 std::map<std::string, int> ranks; // 用户段位 std::queue<SOCKET> waitingPlayers; std::mutex mtx; void sendMsg(SOCKET sock, const std::string &msg) { send(sock, msg.c_str(), msg.size() + 1, 0); } std::string recvMsg(SOCKET sock) { char buffer[BUF_SIZE] = {0}; int bytes = recv(sock, buffer, BUF_SIZE, 0); if (bytes <= 0) return ""; return std::string(buffer); } std::string generateQuestion(int &ans) { int a = rand() % 100 + 1; int b = rand() % 100 + 1; int op = rand() % 4; std::string question; switch(op) { case 0: question = std::to_string(a) + " + " + std::to_string(b) + " = ?"; ans = a + b; break; case 1: question = std::to_string(a) + " - " + std::to_string(b) + " = ?"; ans = a - b; break; case 2: question = std::to_string(a) + " * " + std::to_string(b) + " = ?"; ans = a * b; break; case 3: question = std::to_string(a * b) + " / " + std::to_string(b) + " = ?"; ans = a; break; } return question; } void handleClient(SOCKET clientSocket) { std::string username, password; std::string response; // 注册或登录 sendMsg(clientSocket, "请输入用户名:"); username = recvMsg(clientSocket); sendMsg(clientSocket, "请输入密码:"); password = recvMsg(clientSocket); if (users.find(username) == users.end()) { users[username] = password; ranks[username] = 1; // 初始段位青铜 sendMsg(clientSocket, "注册成功!"); } else if (users[username] != password) { sendMsg(clientSocket, "密码错误!"); closesocket(clientSocket); return; } else { sendMsg(clientSocket, "登录成功!"); } // 首页 std::string homepage = "=== 算术王者 ===\n" "段位: " + std::to_string(ranks[username]) + "\n" "版本: v1.0\n" "按回车开始游戏..."; sendMsg(clientSocket, homepage); recvMsg(clientSocket); // 模拟点击开始 // 匹配玩家 mtx.lock(); waitingPlayers.push(clientSocket); mtx.unlock(); while (true) { mtx.lock(); if (waitingPlayers.size() >= 2) { SOCKET p1 = waitingPlayers.front(); waitingPlayers.pop(); SOCKET p2 = waitingPlayers.front(); waitingPlayers.pop(); mtx.unlock(); // 开始对战 sendMsg(p1, "匹配成功!开始答题!"); sendMsg(p2, "匹配成功!开始答题!"); srand(time(0)); int correct1 = 0, correct2 = 0; double time1 = 0, time2 = 0; for (int i = 0; i < 10; ++i) { int answer; std::string question = generateQuestion(answer); std::string q = "第" + std::to_string(i+1) + "题: " + question; sendMsg(p1, q); sendMsg(p2, q); clock_t start = clock(); std::string res1 = recvMsg(p1); time1 += (double)(clock() - start) / CLOCKS_PER_SEC; int ans1 = atoi(res1.c_str()); if (ans1 == answer) correct1++; sendMsg(p2, q); start = clock(); std::string res2 = recvMsg(p2); time2 += (double)(clock() - start) / CLOCKS_PER_SEC; int ans2 = atoi(res2.c_str()); if (ans2 == answer) correct2++; } std::ostringstream oss; oss << "比赛结束!\n玩家1用时:" << time1 << "秒,答对" << correct1 << "题\n" << "玩家2用时:" << time2 << "秒,答对" << correct2 << "题\n"; if ((correct1 > correct2) || (correct1 == correct2 && time1 < time2)) { oss << "玩家1获胜!段位提升至白银!"; ranks[username] = 2; } else { oss << "玩家2获胜!段位提升至白银!"; ranks[username] = 2; } sendMsg(p1, oss.str()); sendMsg(p2, oss.str()); } else { mtx.unlock(); Sleep(1000); } } } int main() { WSADATA wsa; WSAStartup(MAKEWORD(2, 2), &wsa); SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0); sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(PORT); serverAddr.sin_addr.s_addr = INADDR_ANY; bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)); listen(serverSocket, SOMAXCONN); std::cout << "服务器启动中,等待连接...\n"; while (true) { SOCKET clientSocket = accept(serverSocket, nullptr, nullptr); std::thread(handleClient, clientSocket).detach(); } closesocket(serverSocket); WSACleanup(); return 0; } ``` --- ## ✅ 二、客户端代码(client.cpp) ```cpp #include <iostream> #include <winsock2.h> #include <ws2tcpip.h> #include <string> #pragma comment(lib, "ws2_32.lib") #define SERVER_IP "127.0.0.1" #define PORT 8888 #define BUF_SIZE 1024 void sendMsg(SOCKET sock, const std::string &msg) { send(sock, msg.c_str(), msg.size() + 1, 0); } std::string recvMsg(SOCKET sock) { char buffer[BUF_SIZE] = {0}; int bytes = recv(sock, buffer, BUF_SIZE, 0); if (bytes <= 0) return ""; return std::string(buffer); } int main() { WSADATA wsa; WSAStartup(MAKEWORD(2, 2), &wsa); SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(PORT); inet_pton(AF_INET, SERVER_IP, &serverAddr.sin_addr); if (connect(sock, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { std::cerr << "连接失败!\n"; return 1; } std::string msg; while (!(msg = recvMsg(sock)).empty()) { std::cout << msg << std::endl; if (msg.find("开始答题") != std::string::npos) { // 游戏开始 while (!(msg = recvMsg(sock)).empty()) { if (msg.find("比赛结束") != std::string::npos) { std::cout << msg << std::endl; break; } std::cout << msg << std::endl; int answer; std::cin >> answer; sendMsg(sock, std::to_string(answer)); } } else { std::string input; std::getline(std::cin, input); sendMsg(sock, input); } } closesocket(sock); WSACleanup(); return 0; } ``` --- ## ✅ 功能说明 ### 📌 服务器端功能: - 使用 Winsock 建立 TCP 服务端 - 支持用户注册和登录 - 控制台首页显示段位、版本、开始按钮 - 匹配系统(两个玩家一组) - 创建房间并发送 10 道算术题 - 计时答题,比较速度和正确率 - 胜者段位提升(青铜 → 白银) ### 📌 客户端功能: - 连接服务器 - 输入用户名和密码 - 显示首页信息 - 按回车开始游戏 - 接收题目并输入答案 - 显示比赛结果 --- ## ✅ 编译运行 ### 编译服务器: ```bash g++ server.cpp -o server -lws2_32 ``` ### 编译客户端: ```bash g++ client.cpp -o client -lws2_32 ``` ### 运行顺序: 1. 先运行 `server.exe` 2. 再运行 `client.exe` 多次模拟多个玩家 --- ##
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

值引力

持续创作,多谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值