最近需要用到进程间通信模式处理消息,Socket是一种方便的网络进程通信工具,奈何作为初学者,无法领悟到里面的精髓。
通用的代码网上一搜一大堆:
//服务端(Server)
#include <stdio.h>
#include <iostream>
#include "conio.h"
#include "windows.h"
#include "winsock.h"
//socket库的lib
#pragma comment(lib,"ws2_32.lib")
void main()
{
//初始化socket库
WSADATA wsa = {0}; //WinSockApi 取WSA+DATA组成套接字结构体
WSAStartup(MAKEWORD(2,2),&wsa);
SOCKET socksvr;
//SOCKET sockclient;
/***************创建服务器端套接字SOCKET*******************/
/*******socket()函数解释:IP协议族,数据流方式,TCP协议****/
socksvr=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(INVALID_SOCKET == socksvr)
{
return;
}
/*************建立服务器端套接字地址***********************/
/********************绑定IP和端口号******************/
struct sockaddr_in svraddr = {0};
svraddr.sin_family = AF_INET;//代表internet协议族
svraddr.sin_port = htons(5678);
svraddr.sin_addr.S_un.S_addr = inet_addr("172.16.20.111")/*htonl(INADDR_ANY)*/;//此宏为0,当前机器上任意IP地址,也可以指定当前机的ip和端口。
//绑定,将服务器端套接字与服务器端套接字地址绑定
bind(socksvr,(struct sockaddr *)&svraddr,sizeof(svraddr));//指定名字,类型,长度。绑定套接字。
//侦听
int state = listen(socksvr,SOMAXCONN);//第一个参数是套接字,第二个参数是等待连接队列的最大长度。
//等候客户端建立连接
printf("等候客户端.......\n");
//建立客户端套接字地址,主要是为了接收客户端返回参数之用
struct sockaddr_in clientaddr = {0};
int nLen = sizeof(clientaddr);
while(true)
{
SOCKET sockclient = accept(socksvr,(struct sockaddr*)&clientaddr,&nLen);//建立连接函数
printf("客户端已连接\n");
/********以下是数据收发部分*********/
//先接收后发送,由上面知,数据已在sockclient中,我们只需读此结构便可知晓数据
CHAR szText[100] = {0};
//接收缓冲区数据
recv(sockclient,szText,nLen,0); //接收函数,一直处于侦听模式,等待服务器端发送数据的到来。
printf("%s\n",szText);
//关闭socket
closesocket(sockclient);
}
//closesocket(sockclient);
closesocket(socksvr);
//清理套接字资源
WSACleanup();
//Client
#include <stdio.h>
#include "conio.h"
#include "windows.h"
#include "winsock.h"
#pragma comment(lib,"ws2_32.lib")
void main()
{
//初始化socket库
WSADATA wsa = {0}; //WinSockApi 取WSA+DATA组成套接字结构体
WSAStartup(MAKEWORD(2,2),&wsa);
//创建socket
SOCKET sockclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == sockclient)
{
return;
}
//连接服务器,建立服务器端套接字地址
struct sockaddr_in addr = { 0 };
addr.sin_family = AF_INET;
addr.sin_port = htons(5678);
addr.sin_addr.S_un.S_addr = inet_addr("172.16.20.111");//本机ip
//向服务器发出连接请求,当然我们也可以通过connet函数的返回值判断到底有无连接成功。
int iRetVal = connect(sockclient, (struct sockaddr*)&addr, sizeof(addr));
if (SOCKET_ERROR == iRetVal)
{
printf("服务器连接失败!");
closesocket(sockclient);
return;
}
printf("服务器连接成功!\n");
//数据收发
int num = 0;
while(1)
{
CHAR szSend[100] = "hello server"; //客户端 先发后收
int ret = send(sockclient, szSend, sizeof(szSend), 0); //发送函数,可以通过返回值判断发送成功与否。
}
// getch();//暂停一下
//关闭socket
closesocket(sockclient);
//清理套接字资源
WSACleanup();
}
上面的模式有个问题:只能单客户端通信、单次接收消息。
原因:服务端的accept()阻塞了。
解决策略:服务端建立两个线程,主线程用来监听客户端的接入,子线程实现服务器与客户端的通信。
参考了:https://blog.csdn.net/qq_37041925/article/details/83064164#commentBox
//Server
#include <stdio.h>
#include <iostream>
#include "conio.h"
#include "windows.h"
#include "winsock.h"
//socket库的lib
#pragma comment(lib,"ws2_32.lib")
#define MaxClient 10
#define MaxBufSize 1024
#define PORT 5678
DWORD WINAPI ServerThread(LPVOID lpParameter)
{
SOCKET *ClientSocket=(SOCKET*)lpParameter;
int receByt=0;
char RecvBuf[MaxBufSize];
while(1)
{
receByt = recv(*ClientSocket,RecvBuf,sizeof(RecvBuf),0);
//buf[receByt]='\0';
if(receByt>0){
std::cout<<"接收到的消息是:"<<RecvBuf<<std::endl;
}
else
{
break;
}
memset(RecvBuf,0,sizeof(RecvBuf));
}
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)
{
std::cout<<"端口绑定失败!"<<std::endl;
return -1;
}
int l =listen(ListenSocket,SOMAXCONN);
while(1)
{
//循环接收客户端连接请求并创建服务线程
SOCKET *ClientSocket = new SOCKET;
ClientSocket=(SOCKET*)malloc(sizeof(SOCKET));
//接收客户端连接请求
int SockAddrlen = sizeof(sockaddr);
*ClientSocket = accept(ListenSocket,0,0);
CreateThread(NULL,0,&ServerThread,ClientSocket,0,NULL);
}
closesocket(ListenSocket);
WSACleanup();
return(0);
}
于是实现了多客户端与服务端的通信!