转自:http://blog.csdn.net/neicole/article/details/7539444 并加以改进

Server程序:

 // OneServerMain.cpp

 #include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <iterator>
#include <algorithm>
#include <Winsock2.h>
#include <Windows.h> using namespace std;
HANDLE bufferMutex; // 令其能互斥成功正常通信的信号量句柄
SOCKET sockConn; // 客户端的套接字
vector <SOCKET> clientSocketGroup; DWORD WINAPI SendMessageThread(LPVOID IpParameter);
DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter); int main()
{
// 加载socket动态链接库(dll)
WORD wVersionRequested;
WSADATA wsaData; // 该结构用于接收Wjndows Socket的结构信息
wVersionRequested = MAKEWORD( , ); // 请求2.2版本的WinSock库
int err = WSAStartup( wVersionRequested, &wsaData );
if ( err != )
{
return -; // 返回值为零的时候是表示成功申请WSAStartup
}
if ( LOBYTE( wsaData.wVersion ) != || HIBYTE( wsaData.wVersion ) != ) // 检测是否2.2版本的socket库
{
WSACleanup( );
return -;
} // 创建socket操作,建立流式套接字,返回套接字号sockSrv
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, ); // 套接字sockSrv与本地地址相连
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY); // 将INADDR_ANY转换为网络字节序,调用 htonl(long型)或htons(整型)
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(); // 绑定套接字
if(SOCKET_ERROR == bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)))
{
return -;
} // 将套接字设置为监听模式(连接请求),listen()通知TCP服务器准备好接收连接,最大连接数为20
listen(sockSrv, ); cout << "服务器已成功就绪,若服务器想发送信息给客户端,可直接输入内容后按回车.\n"; // 设置信号量的值
bufferMutex = CreateSemaphore(NULL, , , NULL); // 建立一个向所有客户端发送信息的线程
HANDLE sendThread = CreateThread(NULL, , SendMessageThread, NULL, , NULL); while(true)// 不断等待客户端请求的到来
{
sockConn = accept(sockSrv, NULL, NULL);
if (SOCKET_ERROR != sockConn)
{
clientSocketGroup.push_back(sockConn); //若连接成功则将客户端套接字加入clientSocketGroup
} // 建立一个接收指定客户端信息的线程
HANDLE receiveThread = CreateThread(NULL, , ReceiveMessageThread, (LPVOID)sockConn, , NULL); WaitForSingleObject(bufferMutex, INFINITE); // P操作(资源未被占用则使用资源)
if(NULL == receiveThread)
{
cout << "CreatThread AnswerThread() failed." << endl;
}
else
{
cout << "Create Receive Client Thread OK." << endl;
}
ReleaseSemaphore(bufferMutex, , NULL); // V操作(资源占用完毕将其释放)
} WaitForSingleObject(sendThread, INFINITE); // 等待线程结束
CloseHandle(sendThread);
CloseHandle(bufferMutex);
WSACleanup(); // 终止对套接字库的使用
system("pause");
return ;
} DWORD WINAPI SendMessageThread(LPVOID IpParameter)
{
while()
{
string talk;
getline(cin, talk);
WaitForSingleObject(bufferMutex, INFINITE); // P(资源未被占用) //若服务器要主动关闭
if("quit" == talk)
{
talk.push_back('\0');
for(int i = ; i < clientSocketGroup.size(); ++i)
{
send(clientSocketGroup[i], talk.c_str(), talk.size(), ); // 发送信息
}
ReleaseSemaphore(bufferMutex, , NULL); // V(资源占用完毕)
exit();
} cout << "I Say:(\"quit\"to exit):" << talk << endl; for(int i = ; i < clientSocketGroup.size(); ++i)
{
send(clientSocketGroup[i], talk.c_str(), talk.size(), ); // 发送信息
}
ReleaseSemaphore(bufferMutex, , NULL); // V(资源占用完毕)
}
return ;
} DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter)
{
SOCKET ClientSocket=(SOCKET)(LPVOID)IpParameter;
while()
{
char recvBuf[];
int recv_id = recv(ClientSocket, recvBuf, , ); WaitForSingleObject(bufferMutex, INFINITE); // P操作(资源未被占用) if(recv_id == -)
{
vector<SOCKET>::iterator result = find(clientSocketGroup.begin(), clientSocketGroup.end(), ClientSocket);
clientSocketGroup.erase(result);
closesocket(ClientSocket);
ReleaseSemaphore(bufferMutex, , NULL); // V操作(资源占用完毕)
cout << "Attention: A Client accidentally dropped ..." << endl;
break;
} //若从客户端收到了"quit"命令则将该客户端套接字从clientSocketGroup中清除并关闭
if (recvBuf[] == 'q' && recvBuf[] == 'u' && recvBuf[] == 'i' && recvBuf[] == 't' && recvBuf[] == '\0')
{
vector<SOCKET>::iterator result = find(clientSocketGroup.begin(), clientSocketGroup.end(), ClientSocket);
clientSocketGroup.erase(result);
closesocket(ClientSocket);
ReleaseSemaphore(bufferMutex, , NULL); // V操作(资源占用完毕)
cout << "Attention: A Client has left ..." << endl;
break;
} cout << "One Client Says: " << recvBuf << endl; // 接收信息 ReleaseSemaphore(bufferMutex, , NULL); // V(资源占用完毕)
}
return ;
}

Client程序:

 // MulClientMain.cpp

 #include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <winsock2.h>
#include <Windows.h> using namespace std; SOCKET sockClient; // 连接成功后的套接字
HANDLE bufferMutex; // 令其能互斥成功正常通信的信号量句柄 DWORD WINAPI SendMessageThread(LPVOID IpParameter);
DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter); int main()
{
// 加载socket动态链接库(dll)
WORD wVersionRequested;
WSADATA wsaData; // 这结构是用于接收Wjndows Socket的结构信息的
wVersionRequested = MAKEWORD( , ); // 请求2.2版本的WinSock库
int err = WSAStartup( wVersionRequested, &wsaData );
if ( err != ) // 返回值为零的时候是表示成功申请WSAStartup
{
return -;
}
if ( LOBYTE( wsaData.wVersion ) != || HIBYTE( wsaData.wVersion ) != ) // 检查版本号是否正确
{
WSACleanup( );
return -;
} // 创建socket操作,建立流式套接字,返回套接字号sockClient
sockClient = socket(AF_INET, SOCK_STREAM, );
if(sockClient == INVALID_SOCKET)
{
cout << "Error at socket(): " << WSAGetLastError() << endl;
WSACleanup();
return -;
} SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 本地回路地址是127.0.0.1;
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(); // 将套接字sockClient与远程主机相连
// int connect( SOCKET s, const struct sockaddr* name, int namelen);
// 第一个参数:需要进行连接操作的套接字
// 第二个参数:设定所需要连接的地址信息
// 第三个参数:地址的长度
int con_id = connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
if(con_id == -)
{
cout << "Error at connect(): " << WSAGetLastError() << endl;
WSACleanup();
return -;
} cout << "本客户端已准备就绪,用户可直接输入文字向服务器反馈信息。\n"; bufferMutex = CreateSemaphore(NULL, , , NULL); //创建发送信息与接收信息的线程
HANDLE sendThread = CreateThread(NULL, , SendMessageThread, NULL, , NULL);
HANDLE receiveThread = CreateThread(NULL, , ReceiveMessageThread, NULL, , NULL); WaitForSingleObject(sendThread, INFINITE); // 等待线程结束 closesocket(sockClient);
CloseHandle(sendThread);
CloseHandle(receiveThread);
CloseHandle(bufferMutex);
WSACleanup(); // 终止对套接字库的使用 cout << "End linking..." << endl;
system("pause");
return ;
} DWORD WINAPI SendMessageThread(LPVOID IpParameter)
{
while()
{
string talk;
getline(cin, talk);
WaitForSingleObject(bufferMutex, INFINITE); // P(资源未被占用)
if("quit" == talk)
{
talk.push_back('\0');
send(sockClient, talk.c_str(), talk.size(), );
break;
} cout << "I Say:(\"quit\"to exit):" << talk << endl;
send(sockClient, talk.c_str(), talk.size(), ); // 发送信息
ReleaseSemaphore(bufferMutex, , NULL); // V(资源占用完毕)
}
return ;
} DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter)
{
while()
{
char recvBuf[];
recv(sockClient, recvBuf, , );
WaitForSingleObject(bufferMutex, INFINITE); // P操作(资源未被占用)
if (recvBuf[] == 'q' && recvBuf[] == 'u' && recvBuf[] == 'i' && recvBuf[] == 't' && recvBuf[] == '\0')
{
closesocket(sockClient);
ReleaseSemaphore(bufferMutex, , NULL); // V操作(资源占用完毕)
cout << "Server has been closed ..." << endl;
exit(-);
}
cout << "Server Says: " << recvBuf << endl; // 接收信息
ReleaseSemaphore(bufferMutex, , NULL); // V操作(资源占用完毕)
}
return ;
}

[socket编程] 一个服务器与多个客户端之间通信的更多相关文章

  1. 计算机网络|C语言Socket编程,实现两个程序间的通信

    C语言Socket编程,实现两个程序间的通信 server和client通信流程图 在mooc上找到的,使用Socket客户端client和服务端server通信的流程图

  2. Linux下网络socket编程——实现服务器(select)与多个客户端通信

    一.关于socket通信 服务器端工作流程: 调用 socket() 函数创建套接字 用 bind() 函数将创建的套接字与服务端IP地址绑定 调用listen()函数监听socket() 函数创建的 ...

  3. socket编程——一个简单的例子

    从一个简单的使用TCP例子开始socket编程,其基本步骤如下: server                                                  client ++++ ...

  4. socket编程——一个简单的样例

    从一个简单的使用TCP样例開始socket编程,其基本过程例如以下: server                                                  client ++ ...

  5. Socket编程之聊天程序 - 模拟Fins/ModBus协议通信过程

    设备控制软件编程涉及到的基本通信方式主要有TCP/IP与串口,用到的数据通信协议有Fins与ModBus. 更高级别的通信如.net中的Remoting与WCF在进行C/S架构软件开发时会采用. 本篇 ...

  6. java socket 一个服务器对应多个客户端,可以互相发送消息

    直接上代码,这是网上找的demo,然后自己根据需求做了一定的修改.代码可以直接运行 服务器端: package socket; import java.io.BufferedReader; impor ...

  7. socket编程---一个简单例子

    服务器端代码(单线程): import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamRe ...

  8. socket编程 ------ UDP服务器

    void vLANcommunication( void *pvParameters ) { int32 listenfd; do{ listenfd = socket(AF_INET, SOCK_D ...

  9. C#利用服务器实现客户端之间通信

    这两天在学习C#,C#高级编程真的是厚厚的一本书QAQ. 昨天看了一下里面的通信部分(其实还没怎么看),看了网上一些人的博客,自己在他们的博客基础上写了一个通信. 先来讲述下我自己对于整个Socket ...

随机推荐

  1. (数据科学学习手札40)tensorflow实现LSTM时间序列预测

    一.简介 上一篇中我们较为详细地铺垫了关于RNN及其变种LSTM的一些基本知识,也提到了LSTM在时间序列预测上优越的性能,本篇就将对如何利用tensorflow,在实际时间序列预测任务中搭建模型来完 ...

  2. vue中cssModules理解可以用于加密和避免重复使用

    cssModules可以用于加密和避免重复使用,也就是说可以在当前vue文件中写的样式会生成独一无二的名字,在其他vue文件中是无法调用的, 一.可以直接配cssModules 第一步,配置vue-l ...

  3. 北京Uber优步司机奖励政策(4月3日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  4. 佛山Uber优步司机奖励政策(12月14日到12月20日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  5. COGS:1822. [AHOI2013]作业

    1822. [AHOI 2013] 作业 ★★★   输入文件:ahoi2013_homework.in   输出文件:ahoi2013_homework.out   简单对比时间限制:20 s   ...

  6. LeetCode: 62. Unique Paths(Medium)

    1. 原题链接 https://leetcode.com/problems/unique-paths/description/ 2. 题目要求 给定一个m*n的棋盘,从左上角的格子开始移动,每次只能向 ...

  7. SpringBoot-05:SpringBoot初运行以及tomcat端口号的修改

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 上篇博客讲了,如何创建SpringBoot工程,所以,我本篇博客讲述,如何跑起来自己的第一个案例 1.准备一个 ...

  8. #define NULL ((void *)0)引起的风波

    1. 看下宏定义的结构体 typedef struct { ]; //CMEI/IMEI ]; //server ]; //CMEI/IMEI } Options; 2. 定义的NULL #defin ...

  9. CC3200底板测试-烧写CC3200-LAUNCHXL

    1. 拿到板子,先研究一下几个跳线帽的作用.我在底板上测到VCC_DCDC_3V3和VCC_BRD之间应该有一个跳线帽的,但是在原理上找不到. 2. LED灯的用途,测试的时候,发现这个灯有时候亮,有 ...

  10. 使用GC 初始化DG(将备份集复制到目标端再初始化)

    概述 当前环境中有一个GC节点,一套RAC 11.2.0.4的数据库,一个已经使用GC进行在线初始化好的dg环境,需要模拟在远端使用rman备份集进行初始化DG的操作.   恢复环境 当前环境中 已经 ...