看过我的简单版的服务器代码的,会发现那段代码同一时间只能和一个客户端通信。这样的代码能力很小局限性很大。今天我来介绍一种多客户端的服务器代码。当然这段代码还是有问题的,至于是什么问题我会在代码后面说清楚。
我的这个多客户端的代码核心思想是多线程。在基本的服务器的代码中简单加一些内容就可以了。在accept的后面,我们每接到一个客户端的连接请求,就会为这个客户端创建一个单独的线程,主线程继续循环监听其他连接请求,而不用停留在那里单独为这一个客户端服务。
与此同时我们还要考虑一些其他问题,是不是每个客户端连接上来我们都要给他创建一个线程呢。显然是不能的,因为系统的socket资源是有限的,线程资源也是有限的。而且每新增一个客户端,就会增多内存的开销,我们还要考虑内存是否够用。所以要根据设备的实际能力来判断他的最大连接数量(当然如果只是为了了解原理,实现基本功能,将最大连接数设置为几百上千个对于一般机器来言是没有任何问题的)。也正是因为系统资源的宝贵,我们还要做好回收工作。比如不用的socket我们要及时close,线程结束后还要及时关闭句柄。
另外一点,我们还要掌握所有客户端线程的控制权,所以我们要有一个结构体来存储这些客户端的基本信息,包括他所占线程的ID,套接字等等以及一些实用性的属性。就好像你要写一个csdn的服务器,你不仅要记录线程id和套接字,你还要记录用户名、会员等信息。
我的多线程是用Windows的句柄创建的。但我个人建议还是使用pthread。因为C语言在linux上的使用非常广泛。用pthread写线程函数可移植性更强,跨平台的时候修改的地方更少。这里我先使用windows句柄来实现代码。以后会都使用pthread。
#include <stdio.h>
#include <winsock2.h>
#include <windows.h>
#define MAX_CLIENT_NUMS 999
#pragma comment(lib,"ws2_32.lib")
typedef struct client_list_node
{
SOCKET socket_client; //客户端的socket
struct sockaddr_in c_sin; //用于存储已连接的客户端的socket基本信息
int is_run; //标记这个节点的socket是否正在被使用
HANDLE h; //为这个socket创建的线程 的句柄
}client_list_node_st, *client_list_node_t;
static client_list_node_st client_list[MAX_CLIENT_NUMS] = {
0}; //客户端列表
static SOCKET socket_of_server; //服务端(本地)的socket
static struct sockaddr_in s_sin; //用于存储本地创建socket的基本信息
static int client_nums = 0; //当前连接服务器的客户端的个数
static void analysis(char* data, int datal, client_list_node_t node_t);
DWORD WINAPI myfun1(LPVOID lpParameter); //声明线程函数
int main(