一、前言
主要功能是实现并发的“客户端–服务器”通信。这里有两种方式,分别是TCP和UDP。这里是一个基本的通信代码,读者可以根据自己的需要稍作修改。
二、主要思路
1、TCP:
监听套接字中的accept函数返回一个连接套接字,就用这个套接字作为参数区分不同的客户端。
2、UDP:
对于服务器,第一个来访的客户端新开一个进程,设置端口为6001,之后的端口依次设置为6002、6003…然后告知每个客户端对应的端口重新绑定端口,之后每个客户端都与对应的端口、对应的进程通信。
即根据端口号区别通信的客户端。
改进:如果不想写死端口号,可以系统分配然后再获取端口号,这里没有实现。
三、源代码
1、TCP并发“服务器-客户端”
这个程序中TCP的交互中对应客户端每个消息都需要回应一句。
(1)TCP服务器端
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#if !defined(AFX_STDAFX_H__E01B4F25_9E02_42E0_9E68_09D08D1C776F__INCLUDED_)
#define AFX_STDAFX_H__E01B4F25_9E02_42E0_9E68_09D08D1C776F__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// TODO: reference additional headers your program requires here
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_STDAFX_H__E01B4F25_9E02_42E0_9E68_09D08D1C776F__INCLUDED_)
#include <stdio.h>
#include <cstdio>
#include<iostream>
#include<string.h>
#include<WinSock2.h>
#include <sys/types.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
const int PORT = 8000;
#define MaxClient 10
#define MaxBufSize 1024
#define _CRT_SECURE_NO_WARINGS
//服务线程
DWORD WINAPI ServerThread(LPVOID lpParameter) {
SOCKET *ClientSocket = (SOCKET*)lpParameter;
int receByt = 0;
char RecvBuf[MaxBufSize];
char SendBuf[MaxBufSize];
while (1) {
receByt = recv(*ClientSocket, RecvBuf, sizeof(RecvBuf), 0);
//buf[receByt]='\0';
if (receByt > 0) {
cout << "从" << *ClientSocket << "接收到的消息是:" << RecvBuf << endl;
// cout<<receByt<<endl;
}
else
{
cout << "与" << *ClientSocket << "接收消息结束,连接线程关闭!" << endl;
break;
}
memset(RecvBuf, 0, sizeof(RecvBuf));
cout << "请输入要发送到客户端" << *ClientSocket << "的信息(q退出):" << endl;
gets_s(SendBuf);
int k = 0;
k = send(*ClientSocket, SendBuf, sizeof(SendBuf), 0);
if (k < 0) {
cout << "发送到" << *ClientSocket << "消息失败" << endl;
}
if (*SendBuf == 'q')
break;
memset(SendBuf, 0, sizeof(SendBuf));
}//while
cout << "与" << *ClientSocket << "之间连接线程关闭" << endl;
closesocket(*ClientSocket);
free(ClientSocket);
return 0;
}
int main() {
WSAData wsd;
WSAStartup(MAKEWORD(2, 2), &wsd);
SOCKET ListenSocket = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN ListenAddr;
ListenAddr.sin_family = AF_INET;
ListenAddr.sin_addr.S_un.S_addr = INADDR_ANY;//表示填入本机ip
ListenAddr.sin_port = htons(PORT);
int n;
n = bind(ListenSocket, (LPSOCKADDR)&ListenAddr, sizeof(ListenAddr));
if (n == SOCKET_ERROR) {
cout << "listen端口绑定失败!" << endl;
return -1;
}
else {
cout << "listen端口绑定成功:" << PORT << endl;
}
int l = listen(ListenSocket, 20);
cout << "服务端listening......" << endl;
int close_l = 0;
while (1) {
//循环接收客户端连接请求并创建服务线程
SOCKET *ClientSocket = new SOCKET;
ClientSocket = (SOCKET*)malloc(sizeof(SOCKET));
//接收客户端连接请求
*ClientSocket = accept(ListenSocket, 0, 0);
cout << "一个客户端已连接到服务器,socket返回值是:" << *ClientSocket << endl;
CreateThread(NULL, 0, &ServerThread, ClientSocket, 0, NULL);
}//while
closesocket(ListenSocket);
WSACleanup();
return(0);
}//main
(2)TCP服务器端
#ifndef _CLIENT_H
#define _CLIENT_H
#include <winsock2.h>
#define TIMEFOR_THREAD_CLIENT 500 //线程睡眠时间
#define MAX_NUM_CLIENT 10 //接受的客户端连接最多数量
#define MAX_NUM_BUF 48 //缓冲区的最大长度
#define INVALID_OPERATOR 1 //无效的操作符
#define INVALID_NUM 2 //分母为零
#define ZERO 0 //零
//数据包类型
#define EXPRESSION 'E' //算数表达式
#define BYEBYE 'B' //消息byebye
#define HEADERLEN (sizeof(hdr)) //头长度
//数据包头结构,该结构在win32下为4byte
typedef struct _head
{
char type; //类型
unsigned short len; //数据包的长度(包括头的长度)
}hdr, *phdr;
//数据包中的数据结构
typedef struct _data
{
char buf[MAX_NUM_BUF];//数据
}DATABUF, *pDataBuf;
class CClient
{
public:
CClient(const SOCKET sClient, const sockaddr_in &addrClient);
virtual ~CClient();
public:
BOOL StartRuning(void); //创建发送和接收数据线程
void HandleData(const char* pExpr); //计算表达式
BOOL IsConning(void) { //是否连接存在
return m_bConning;
}
void DisConning(void) { //断开与客户端的连接
m_bConning = FALSE;
}
BOOL IsExit(void) { //接收和发送线程是否已经退出
return m_bExit;
}
public:
static DWORD __stdcall RecvDataThread(void* pParam); //接收客户端数据
static DWORD __stdcall SendDataThread(void* pParam); //向客户端发送数据
private:
CClient();
private:
SOCKET m_socket; //套接字
sockaddr_in m_addr; //地址
DATABUF m_data; //数据
HANDLE m_hEvent; //事件对象
HANDLE m_hThreadSend; //发送数据线程句柄
HANDLE m_hThreadRecv; //接收数据线程句柄
CRITICAL_SECTION m_cs; //临界区对象
BOOL m_bConning; //客户端连接状态
BOOL m_bExit; //线程退出
};
#endif
#include <cstdio>
#include<iostream>
#include<string>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
const int PORT = 8000;
#define MaxBufSize 1024
#define _CRT_SECURE_NO_WARINGS
int main() {
WSADATA wsd;
WSAStartup(MAKEWORD(2, 2), &wsd);
SOCKET SocketClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
SOCKADDR_IN ClientAddr;
ClientAddr.sin_family = AF_INET;
ClientAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
ClientAddr.sin_port = htons(PORT);
int n = 0;
n = connect(SocketClient, (struct sockaddr*)&ClientAddr, sizeof(ClientAddr));
if (n == SOCKET_ERROR) {
cout << "连接失败" << endl;
return -1;
}
cout << "已经连接到服务器" << n << ",可以向服务器发送消息了!" << endl;
char info[1024], SendBuff[MaxBufSize], RecvBuff[MaxBufSize];
while (1) {
cout << "请输入要发送的信息,按回车结束发送(q退出):" << endl;
gets_s(info);
if (*info == 'q')
break;
strcpy(SendBuff, info);
memset(info, 0, sizeof(info));
n = send(SocketClient, SendBuff, sizeof(SendBuff), 0);
memset(SendBuff, 0, sizeof(SendBuff));
if (n < 0) {
cout << "发送失败" << endl;
}
Sleep(500);
int n = 0;
n = recv(SocketClient, RecvBuff, sizeof(RecvBuff), 0);
if (n > 0) {
cout << "接收到服务器的消息为:" << RecvBuff << endl;
}
if (*RecvBuff == 'q')
break;
memset(RecvBuff, 0, sizeof(RecvBuff));
}
cout << "与服务器连接线程关闭" << endl;
closesocket(SocketClient);
WSACleanup();
return 0;
}
2、UDP并发“服务器-客户端”
这个程序中UDP的交互中对应客户端每个消息不必回应。
(1)UDP服务器端
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#if !defined(AFX_STDAFX_H__E01B4F25_9E02_42E0_9E68_09D08D1C776F__INCLUDED_)
#define AFX_STDAFX_H__E01B4F25_9E02_42E0_9E68_09D08D1C776F__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// TODO: reference additional headers your program requires here
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_STDAFX_H__E01B4F25_9E02_42E0_9E68_09D08D1C776F__INCLUDED_)
#include <stdio.h>
#include <cstdio>
#include<sstream>
#include<iostream>
#include<string.h>
#include<WinSock2.h>
#include <sys/types.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
const int PORT = 8000;
#define MaxClient 10
#define MaxBufSize 1024
#define _CRT_SECURE_NO_WARINGS
//服务线程
DWORD WINAPI ServerThread(LPVOID lpParameter) {
int port = *(int*)lpParameter;
port--;
SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, 0);
//创建地址结构体.
SOCKADDR_IN addrClient;
addrClient.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrClient.sin_family = AF_INET;
addrClient.sin_port = htons(port);
//绑定套接字和地址.
bind(sockClient, (SOCKADDR *)&addrClient, sizeof(SOCKADDR));
char recvBuf[100];
char sendBuf[100];
memset(recvBuf, 0, sizeof(recvBuf) / sizeof(char));
memset(sendBuf, 0, sizeof(sendBuf) / sizeof(char));
int len = sizeof(SOCKADDR);
int i = 0;
while (i < 20) {
i++;
recvfrom(sockClient, recvBuf, 100, 0, (SOCKADDR *)&addrClient, &len);
cout << port << " says:" << recvBuf << endl;
if (*recvBuf == 'q') {
sendto(sockClient, "q", 1, 0, (SOCKADDR *)&addrClient, sizeof(SOCKADDR));
cout << "The connection with " << port << " ends!" << endl;
break;
}
ostringstream oss;
oss << "echo:" << recvBuf;
strcpy(sendBuf, oss.str().c_str());
cout << "The data returned to " << port << " is: " << sendBuf << endl;
//发送数据.
sendto(sockClient, sendBuf, 100, 0, (SOCKADDR*)&addrClient, sizeof(SOCKADDR));
}
closesocket(sockClient);
return 0;
}
int main() {
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
return 0;
}
if (LOBYTE(wsaData.wVersion) != 1 ||
HIBYTE(wsaData.wVersion) != 1) {
WSACleanup();
return 0;
}
//创建套接字
SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);
//创建地址结构体.
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
//绑定套接字和地址.
bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
char recvBuf[100];
char sendBuf[100];
SOCKADDR_IN addrClient;
int len = sizeof(SOCKADDR);
int i = 6000;
cout << "listening..." << endl;
while (1) {
i++;
//接收客户端连接请求
recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR *)&addrClient, &len);
cout << "一个客户端已连接到服务器,新端口是:" << i << endl;
string i_s;
stringstream ss;
ss << i;
ss >> i_s;
strcpy(sendBuf, i_s.c_str());
sendto(sockSrv, sendBuf, 100, 0, (SOCKADDR*)&addrClient, sizeof(SOCKADDR));
CreateThread(NULL, 0, &ServerThread, &i, 0, NULL);
}//while
closesocket(sockSrv);
WSACleanup();
return(0);
}//main
(2)UDP客户端
#include <Winsock2.h>
#include <stdio.h>
#include <cstdio>
#include<iostream>
#include<string>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
void main() {
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
return;
}
if (LOBYTE(wsaData.wVersion) != 1 ||
HIBYTE(wsaData.wVersion) != 1) {
WSACleanup();
return;
}
SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, 0);
SOCKADDR_IN addrClient;
addrClient.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addrClient.sin_family = AF_INET;
addrClient.sin_port = htons(6000);
char recvBuf[100];
char sendBuf[100];
int len = sizeof(SOCKADDR);
int i = 0;
while (i < 20) {
i++;
printf("please input date:");
gets_s(sendBuf);
sendto(sockClient, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrClient, len);
if (*sendBuf == 'q')
break;
recvfrom(sockClient, recvBuf, 100, 0, (SOCKADDR*)&addrClient, &len);
cout << "The message from server is:" << recvBuf << endl;
if (i == 1) {
int port = atoi(recvBuf);
cout << "port:" << port << endl;
addrClient.sin_port = htons(port);
}
}
closesocket(sockClient);
WSACleanup();
return;
}