Windows Socket 编程_单个服务器对多个客户端简单通讯
单个服务器对多个客户端程序:
一。简要说明
二。查看效果
三。编写思路
四。程序源代码
五。存在问题
一。简要说明:
程序名为:TcpSocketOneServerToMulClient
程序功能:实现单个服务器对多个客户端通讯功能的小程序。
PS: 这是继上次简单的 Tcp Windows Socket 编程后的再一程序,程序实现依然不是很严谨,还待完善~
二。查看效果:

三。编写思路:

由上一次的程序思路来看,如果想实现单个服务器对多个客户端程序的通讯的话,这次程序编写尝试从多线程的角度来考虑问题:
在服务器的实现中:可以main函数作为主线程,不断地接客户端的连接请求。
再新建子线程——每连接一个客户端,就专为这个客户端新建一个用于实现接收信息并显示到屏幕上功能的子线程。
然后,新建子线程,专用于本机发送消息。
在客户端的实现中:主线程负责连接服务器,新建子线程,用于从服务器接收信息,再建子线程,用于从客户端向服务器中发送信息。
总的来说,也可以理解为,单个服务器的进程利用这个服务器中的线程与多个客户端进程进行通讯。
四。程序源代码:
// 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; 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服务器准备好接收连接
listen(sockSrv, ); cout << "服务器已成功就绪,若服务器想发送信息给客户端,可直接输入内容后按回车.\n";
// accept(),接收连接,等待客户端连接 bufferMutex = CreateSemaphore(NULL, , , NULL); DWORD WINAPI SendMessageThread(LPVOID IpParameter);
DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter); HANDLE sendThread = CreateThread(NULL, , SendMessageThread, NULL, , NULL); while(true){ // 不断等待客户端请求的到来
sockConn = accept(sockSrv, NULL, NULL);
if (SOCKET_ERROR != sockConn){
clientSocketGroup.push_back(sockConn);
}
HANDLE receiveThread = CreateThread(NULL, , ReceiveMessageThread, (LPVOID)sockConn, , NULL);
WaitForSingleObject(bufferMutex, INFINITE); // P(资源未被占用)
if(NULL == receiveThread) {
printf("\nCreatThread AnswerThread() failed.\n");
}
else{
printf("\nCreate Receive Client Thread OK.\n");
}
ReleaseSemaphore(bufferMutex, , NULL); // V(资源占用完毕)
} WaitForSingleObject(sendThread, INFINITE); // 等待线程结束
CloseHandle(sendThread);
CloseHandle(bufferMutex);
WSACleanup(); // 终止对套接字库的使用
printf("\n");
system("pause");
return ;
} DWORD WINAPI SendMessageThread(LPVOID IpParameter)
{
while(){
string talk;
getline(cin, talk);
WaitForSingleObject(bufferMutex, INFINITE); // P(资源未被占用)
/* if("quit" == talk){
ReleaseSemaphore(bufferMutex, 1, NULL); // V(资源占用完毕)
return 0;
}
else*/
{
talk.append("\n");
}
printf("I Say:(\"quit\"to exit):");
cout << talk;
for(int i = ; i < clientSocketGroup.size(); ++i){
// send(clientSocketGroup[i], talk.c_str(), talk.size(), 0); // 发送信息
send(clientSocketGroup[i], talk.c_str(), , ); // 发送信息
}
ReleaseSemaphore(bufferMutex, , NULL); // V(资源占用完毕)
}
return ;
} DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter)
{
SOCKET ClientSocket=(SOCKET)(LPVOID)IpParameter;
while(){
char recvBuf[];
recv(ClientSocket, recvBuf, , );
WaitForSingleObject(bufferMutex, INFINITE); // P(资源未被占用) 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(资源占用完毕)
printf("\nAttention: A Client has leave...\n", , );
break;
} printf("%s Says: %s\n", "One Client", recvBuf); // 接收信息 ReleaseSemaphore(bufferMutex, , NULL); // V(资源占用完毕)
}
return ;
}
// MulClientMain.cpp #include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <winsock2.h>
#include <Windows.h> using namespace std; SOCKET sockClient; // 连接成功后的套接字
HANDLE bufferMutex; // 令其能互斥成功正常通信的信号量句柄 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) {
printf("Error at socket():%ld\n", WSAGetLastError());
WSACleanup();
return -;
} // 将套接字sockClient与远程主机相连
// int connect( SOCKET s, const struct sockaddr* name, int namelen);
// 第一个参数:需要进行连接操作的套接字
// 第二个参数:设定所需要连接的地址信息
// 第三个参数:地址的长度
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();
connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
cout << "本客户端已准备就绪,用户可直接输入文字向服务器反馈信息。\n"; // send(sockClient, "\nAttention: A Client has enter...\n", strlen("Attention: A Client has enter...\n")+1, 0);
send(sockClient, "\nAttention: A Client has enter...\n", , ); bufferMutex = CreateSemaphore(NULL, , , NULL); DWORD WINAPI SendMessageThread(LPVOID IpParameter);
DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter); 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(); // 终止对套接字库的使用 printf("End linking...\n");
printf("\n");
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(), 0);
send(sockClient, talk.c_str(), , );
break;
}
else{
talk.append("\n");
}
printf("\nI Say:(\"quit\"to exit):");
cout << talk;
// send(sockClient, talk.c_str(), talk.size(), 0); // 发送信息
send(sockClient, talk.c_str(), , ); // 发送信息
ReleaseSemaphore(bufferMutex, , NULL); // V(资源占用完毕)
}
return ;
} DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter)
{
while(){
char recvBuf[];
recv(sockClient, recvBuf, , );
WaitForSingleObject(bufferMutex, INFINITE); // P(资源未被占用) printf("%s Says: %s\n", "Server", recvBuf); // 接收信息 ReleaseSemaphore(bufferMutex, , NULL); // V(资源占用完毕)
}
return ;
}
五。存在问题:
1. 将客户端异常退出(不按程序要求输入“quit”退出),而直接用ALT+F4关闭后,服务器会出现死循环显示乱码的情况。
2. 在未启动服务器前提下,直接启动客户端时出现死循环乱码情况。
3. 服务器输入“quit”时不能正常退出。
转http://blog.csdn.net/neicole/article/details/7539444
Windows Socket 编程_单个服务器对多个客户端简单通讯的更多相关文章
- Windows Socket 编程_ 简单的服务器/客户端程序
转载自:http://blog.csdn.net/neicole/article/details/7459021 一.程序运行效果图 二.程序源代码 三.程序设计相关基础知识 1.计算机网络 2 ...
- android下socket编程问题:服务器关闭时,客户端发送请求的异常处理
我用socket分别创建了一个服务器和一个客户端. 当服务器程序运行时,客户端和服务器发送接收数据是OK的. 但是,如果服务器程序关闭以后,客户端仍然发送请求的话,会抛出一个IOException.但 ...
- linux tcp/ip编程和windows tcp/ip编程差别以及windows socket编程详解
最近要涉及对接现有应用visual c++开发的tcp客户端,花时间了解了下windows下tcp开发和linux的差别,从开发的角度而言,最大的差别是头文件(早期为了推广尽可能兼容,后面越来越扩展, ...
- socket编程实现tcp服务器_C/C++
1. 需求分析 实现一个回声服务器的C/S(客户端client/服务器server)程序,功能为客户端连接到服务器后,发送一串字符串,服务器接受信息后,返回对应字符串的大写形式给客户端显示. 例如: ...
- 利用socket编程在ESP32上搭建一个TCP客户端
通过之前http://www.cnblogs.com/noticeable/p/7636582.html中对socket的编程,已经可以知道如何通过socket编程搭建服务器和客户端了,现在,就在ES ...
- windows socket编程select模型使用
int select( int nfds, //忽略 fd_ser* readfds, //指向一个套接字集合,用来检测其可读性 ...
- Windows Socket编程精华《TCP通信服务器》
1.网络中进程之间如何通信? 首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的.其实TCP/IP协议族已经帮我们解决了这个问 ...
- socket编程和并发服务器
socket这个词可以表示很多概念: 在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程,“IP地址+端口号”就称为socket. 在TCP协议中,建立连接的两个进程 ...
- Java修炼——基于TCP协议的Socket编程_双向通信_实现模拟用户登录
首先我们需要客户端和服务器端. 服务器端需要:1.创建ServerSocket对象.2.监听客户端的请求数据.3.获取输入流(对象流)即用户在客户端所发过来的信息. ...
随机推荐
- vi / vim 命令集合
vim的命令太多了,不常用就会忘记,所以我决定把vim的各种命令整理下来,包括vim的插入删除.光标移动.多窗口编辑.复制粘贴.查找替换.以及一些常用命令 删除操作 dd 删除当前行 ndd ...
- Javascript基础语法(一)
一.Javascript简介 1. 定义 基于事件和对象驱动,并具有安全性能的脚本语言. 2. 出现背景 上世纪90年代,在美国有出现,当时有上网的环境,并且有网站服务在运行. 注册服务 上图涉及的问 ...
- WebForm文件上传
用 FileUpload控件进行上传文件. <asp:FileUpload ID="FileUpload1" runat="server" /> ...
- shell里连接数据库,将结果输出到变量
result=$(sqlplus -s 'ccc/ccc@21.96.34.34:1521'<<EOF ..... EOF )
- 【转】LNK1123: 转换到 COFF 期间失败: 文件无效或损坏
用VS2010编译C++项目时出现这样的错误: LNK1123: 转换到 COFF 期间失败: 文件无效或损坏 方案一:(这个方法比较好,在用qt运行时出现问题也能解决) 复制 C:\Windows\ ...
- 问题 1690: 算法4-7:KMP算法中的模式串移动数组
题目链接:https://www.dotcpp.com/oj/problem1690.html 题目描述 字符串的子串定位称为模式匹配,模式匹配可以有多种方法.简单的算法可以使用两重嵌套循环,时间复杂 ...
- [转载]C# TimeSpan 计算时间差(时间间隔)
TimeSpan 结构 表示一个时间间隔. 命名空间:System 程序集:mscorlib(在 mscorlib.dll 中) 说明: 1.DateTime值类型代表了一个从公元0001年1月1日 ...
- 安卓自定义TextView实现自动滚动
xml文件代码 <com.mobile.APITest.ScrollEditText android:id="@+id/statusEditText" android:lay ...
- Xshell连接Linux慢问题解决办法
由于各种原因,经常更换网络环境,然后发现,每次更换网络环境后,xshell连接虚拟机的rhel或者CentOS都几乎是龟速.... 今天专门查了一下解决方案: 原来是ssh的服务端在连接时会自动检测d ...
- centos安装图形界面
1.首先安装X(X Window System),命令为 yum groupinstall "X Window System" 回车 2.由于这个软件组比较大,安装过程会比较 ...