Socket系列(二)基于Socket网络通讯的服务端和客户端程序设计

        Socket系列一主要介绍了Socket的基础知识、工作原理以及与传统的http协议的区别。这部分内容的目的是为本文做铺垫。本文将介绍基于Socket网络通讯的服务器端和客户端的程序设计。

一、服务器端的程序设计

        关于Socket服务器端的内容,在这里只提供设计思路,以及相应的测试界面,不提供具体的代码。系列一中有提到,Socket服务器端的程序编写流程大概有创建套接字(socket)、服务绑定(bind)、服务侦听(listen)、处理新到连接(accept)、数据收发(rec/send)、关闭套接字(close)几个部分。接下来,将介绍如果将这几个部分连接到一起,开发一个完整的Socket服务器端程序。主要步骤如下:

        首先,创建一个套接字,绑定IP和端口号,并设置监听数为10(代表同时最大连接数量);

        其次,因为我用的是Winform写的测试程序,所以创建一个后台线程,在线程中等待客户端的连接请求;

        接着,当接收到请求后,同样创建一个后台线程,在线程中,接收来自此客户端的消息,并将该Socket保存到队列中。另外还需创建和各个客户端的心跳包;

        然后,如果服务端想要推送消息给客户端,可从队列中找出要发送的客户端对象,如果存在该连接,即可实时推送内容到客户端;

        最后,若想关闭某一连接,执行close即可;

        服务端简略效果图,如下所示:

一、客户端的程序设计

        关于Socket客户端的内容,系列一中有提到,Socket客户端的程序编写流程大概有:客户端套接字创建(socket)、发起连接(connect)、数据收发(rec/send)、关闭套接字(close)几个部分。接下来,将介绍如果将这几个部分连接到一起,开发一个完整的Socket客户端程序。主要步骤如下:

        首先,创建一个客户端套接字,设置服务端提供的IP和端口号;

        其次,通过connet与服务端建立连接;

        接着,当建立连接后,创建一个后台线程,在线程中,接收来自此服务端推送的实时消息,并将该Socket保存;

        然后,如果客户端想要发送消息给服务端,如果该连接没有断开,即可通过该Socket实时发送内容到服务端;

        最后,若想关闭该连接,执行close即可;

        客户端简略效果图,如下所示:

 

好的,这是一个比较复杂的问题,需要一步一步地来。我们先来介绍一下Winsock和Socket通讯编程。 Winsock是Windows下的一组网络编程API,它允许程序员编写网络应用程序,实现网络通信Socket通讯编程是一种基于TCP/IP协议的网络编程技术,它允许不同计算机之间进行数据交换。 为了实现多线程的服务端客户端程序,我们需要使用一些线程相关的API,例如CreateThread和WaitForMultipleObjects等。而要实现同时进行发送和接收数据,我们需要使用异步Socket通讯编程技术。 下面是一个简单的多线程服务端程序的代码: ```c // 头文件 #include <windows.h> #include <winsock.h> #include <stdio.h> // 常量定义 #define MAX_CLIENTS 10 #define BUF_SIZE 1024 // 全局变量 SOCKET clients[MAX_CLIENTS]; int num_clients = 0; // 线程函数 DWORD WINAPI ClientThread(LPVOID lpParam) { SOCKET client = (SOCKET)lpParam; char buf[BUF_SIZE]; int ret; while (1) { ret = recv(client, buf, BUF_SIZE, 0); if (ret == SOCKET_ERROR) { printf("recv failed: %d\n", WSAGetLastError()); break; } else if (ret == 0) { printf("client disconnected\n"); break; } else { printf("recv: %s\n", buf); for (int i = 0; i < num_clients; i++) { if (clients[i] != client) { send(clients[i], buf, ret, 0); } } } } closesocket(client); return 0; } // 主函数 int main() { WSADATA wsaData; SOCKET listen_sock, client_sock; SOCKADDR_IN server_addr, client_addr; int ret, len; // 初始化Winsock ret = WSAStartup(MAKEWORD(2, 2), &wsaData); if (ret != 0) { printf("WSAStartup failed: %d\n", ret); return 1; } // 创建Socket listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (listen_sock == INVALID_SOCKET) { printf("socket failed: %d\n", WSAGetLastError()); WSACleanup(); return 1; } // 绑定Socket server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(12345); ret = bind(listen_sock, (SOCKADDR*)&server_addr, sizeof(server_addr)); if (ret == SOCKET_ERROR) { printf("bind failed: %d\n", WSAGetLastError()); closesocket(listen_sock); WSACleanup(); return 1; } // 监听Socket ret = listen(listen_sock, SOMAXCONN); if (ret == SOCKET_ERROR) { printf("listen failed: %d\n", WSAGetLastError()); closesocket(listen_sock); WSACleanup(); return 1; } // 接受连接 while (1) { client_sock = accept(listen_sock, (SOCKADDR*)&client_addr, &len); if (client_sock == INVALID_SOCKET) { printf("accept failed: %d\n", WSAGetLastError()); closesocket(listen_sock); WSACleanup(); return 1; } // 创建线程 if (num_clients < MAX_CLIENTS) { clients[num_clients] = client_sock; num_clients++; CreateThread(NULL, 0, ClientThread, (LPVOID)client_sock, 0, NULL); } else { printf("too many clients\n"); closesocket(client_sock); } } // 关闭Socket closesocket(listen_sock); // 清理Winsock WSACleanup(); return 0; } ``` 这个程序实现了一个简单的多线程服务端,它可以接受多个客户端连接,并且可以同时处理多个客户端的数据。每个客户端连接都会创建一个新的线程,这个线程负责接收客户端发送的数据,并将这些数据发送给其他客户端。 下面是一个简单的客户端程序的代码: ```c // 头文件 #include <windows.h> #include <winsock.h> #include <stdio.h> // 常量定义 #define BUF_SIZE 1024 // 主函数 int main() { WSADATA wsaData; SOCKET sock; SOCKADDR_IN server_addr; char buf[BUF_SIZE]; int ret; // 初始化Winsock ret = WSAStartup(MAKEWORD(2, 2), &wsaData); if (ret != 0) { printf("WSAStartup failed: %d\n", ret); return 1; } // 创建Socket sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == INVALID_SOCKET) { printf("socket failed: %d\n", WSAGetLastError()); WSACleanup(); return 1; } // 连接服务器 server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); server_addr.sin_port = htons(12345); ret = connect(sock, (SOCKADDR*)&server_addr, sizeof(server_addr)); if (ret == SOCKET_ERROR) { printf("connect failed: %d\n", WSAGetLastError()); closesocket(sock); WSACleanup(); return 1; } // 发送数据 while (1) { printf("input message: "); gets(buf); ret = send(sock, buf, strlen(buf), 0); if (ret == SOCKET_ERROR) { printf("send failed: %d\n", WSAGetLastError()); closesocket(sock); WSACleanup(); return 1; } } // 关闭Socket closesocket(sock); // 清理Winsock WSACleanup(); return 0; } ``` 这个程序实现了一个简单的客户端,它可以连接到服务端,并且可以发送数据给服务端客户端程序中没有使用多线程,因为它只需要处理一个连接。 需要注意的是,在实际的应用中,我们应该对Winsock和Socket通讯编程有更深入的了解,并且需要对线程相关的API和异步Socket通讯编程技术有更深入的了解,才能够编写出稳定、高效的多线程服务端客户端程序。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值