1.1 功能结构图
网络通信系统一共由4个模块组成,分别是点对点客户端、点对点服务端、服务器中转服务端、服务器中转客户端。这4个模块是成对使用的,点对点客户端和点对点服务端一起使用,服务器中转服务端和服务器中转客户端一起使用。
功能结构体如下图所示:
1.2 编写程序
网络通信程序分为2个模块实现,点对点模块和服务器中转模块。
common.h
#ifndef __COMMON_H__ #define __COMMON_H__ #include "stdafx.h" #include <stdlib.h> #include <stdio.h> #pragma comment (lib,"ws2_32.lib") //链接ws2_32.dll动态链接库 //客户端发送给服务端的消息类型 #define CLIENTSEND_EXIT 1 #define CLIENTSEND_TRAN 2 #define CLIENTSEND_LIST 3 //服务端发送给客户端的消息类型 #define SERVERSEND_SELFID 1 #define SERVERSEND_NEWUSR 2 #define SERVERSEND_SHOWMSG 3 #define SERVERSEND_ONLINE 4 //定义记录聊天消息的文件指针 extern FILE *server_fp; //记录服务端聊天消息的文件指针 extern FILE *client_fp; //记录客户端聊天消息的文件指针 //服务端接收消息的结构体,客户端使用这个结构发送消息(以客户端为主体) struct CReceivePackage { int iType; //存放消息类型 int iToID; //存放目标用户ID int iFromID; //存放原用户ID char cBuffer[1024]; //存放消息内容 }; //以服务端发送消息的结构体,服务端使用这个结构体发送消息(以服务端为主体) struct CSendPackage { int iType; //消息类型 int iCurConn; //当前在线用户数量 char cBuffer[512]; //存放消息内容 [VS内部限制了堆栈的大小,所以减少为512,避免堆栈溢出] }; //服务端存储在线用户数据的结构体 struct CUserSocketInfo { int ID; //用户的ID char cDstIP[64]; //用户的IP地址,扩展使用 int iPort;//用户应用程序端口扩展使用 SOCKET sUserSocket; //网络句柄 }; //客户端存储在线用户列表的结构体 struct CUser { int ID; //用户的ID char cDstIP[64]; //用户的IP地址 扩展时使用 }; extern struct CUser usr[20]; //客户端存储用户信息的对象 extern int bSend;//是否可以发送消息 extern int iMyself;//自己的id extern int iNew;//在线用户数 extern int CheckIP(char *cIP); //检查IP地址 extern struct CUserSocketInfo usrinfo[20]; //服务端存储用户信息的对象 #endif
common.cpp
#include "stdafx.h" #include <WinSock2.h> //包含socket套接字的API函数 #include "common.h" //定义记录聊天消息的文件指针 FILE *server_fp; //记录服务端聊天消息的文件指针 FILE *client_fp; //记录客户端聊天消息的文件指针 struct CUser usr[20]; //客户端存储用户信息的对象 int bSend=0;//是否可以发送消息 int iMyself;//自己的id int iNew=0;//在线用户数 struct CUserSocketInfo usrinfo[20]; //服务端存储用户信息的对象 /* 函数功能:检查IP地址 详细介绍:检查IP地址中的点是否是3个,以及每段IP的数值是否超过255 */ int CheckIP(char *cIP) { char IPAddress[128];//IP地址字符串 char IPNumber[4];//IP地址中每组的数值 int iSubIP=0;//IP地址中4段之一 int iDot=0;//IP地址中‘.‘的个数 int iResult=0; int iIPResult=1; int i;//循环控制变量 memset(IPNumber,0,4); strncpy(IPAddress,cIP,128); for(i=0;i<128;i++) { if(IPAddress[i]==‘.‘) { iDot++; iSubIP=0; if(atoi(IPNumber)>255) //检查每段IP的数值是否超过255 iIPResult = 0; memset(IPNumber,0,4); } else { IPNumber[iSubIP++]=IPAddress[i]; } if(iDot==3 && iIPResult!=0) //检查IP地址中的点是否是3个 iResult= 1; } return iResult; }
pointToPointModule.h
#ifndef __pointToPointModule_H__ #define __pointToPointModule_H__ #include "stdafx.h" #include "common.h" extern void createServer(); //创建点对点服务端 extern void createClient(); //创建点对点客户端 #endif
pointToPointModule.cpp [点对点模块]
#include "stdafx.h" #include <WinSock2.h> //包含socket套接字的API函数 #include "pointToPointModule.h" /* 函数功能:退出系统函数,并释放文件指针和ws2_32.lib动态链接库 */ void ExitSystem() { if(server_fp!=NULL) fclose(server_fp); if(client_fp!=NULL) fclose(client_fp); WSACleanup(); //释放初始化ws2_32.lib动态链接库所分配的资源 exit(0); } /* 函数功能:创建客户端接收消息的线程 */ DWORD WINAPI threadproClient(LPVOID lpParam) { SOCKET hsock=(SOCKET)lpParam; char cRecvBuffer[1024]; //接收消息缓存,接收数据保存在cRecvBuff[] char cShowBuffer[1024]; //显示消息缓存 int recCharNum = 0; if(hsock!=INVALID_SOCKET) printf("start:\n"); while(1) { recCharNum = recv(hsock,cRecvBuffer,1024,0); if(recCharNum >= 0) { cRecvBuffer[recCharNum]=‘\0‘; sprintf(cShowBuffer,"to me : %s\n",recCharNum); printf("%s",cShowBuffer); fwrite(cShowBuffer ,sizeof(char),strlen(cShowBuffer),client_fp); fflush(client_fp); if(strcmp("exit",cRecvBuffer)==0) { ExitSystem(); } } } return 0; } /* 函数功能:创建服务端接收消息的线程 */ DWORD WINAPI threadproServer(LPVOID lpParam) // LPVOID lpParameter为线程参数 { SOCKET hsock = (SOCKET)lpParam; char cRecvBuffer[1024]; //接收消息缓存,接收数据保存在cRecvBuff[] char cShowBuffer[1024]; //显示消息缓存 int iRecvResult = 0; if(hsock != INVALID_SOCKET) { printf("start:\n"); } while(1) { iRecvResult = recv(hsock,cRecvBuffer,1024,0); if(iRecvResult >= 0) { cRecvBuffer[iRecvResult] = ‘\0‘; //将cRecvBuff[]变为字符串 sprintf(cShowBuffer,"to me:%s\n",cRecvBuffer); //sprintf: 格式化的数据写入某个字符串中 printf("%s",cShowBuffer); //显示接收到的数据 fwrite(cShowBuffer,1,strlen(cShowBuffer),server_fp); //将接收到的数据,写入到服务端文件中 fflush(server_fp); //刷新文件流 stream 的输出缓冲区 (文件指针本质也是一种流stream) if(strcmp("exit",cRecvBuffer) == 0) { ExitSystem(); //退出系统函数,并释放文件指针和ws2_32.lib动态链接库 //退出系统 } } } return 0; } /* 函数功能:创建点对点服务端 详细介绍:服务端监听客服端发来的连接请求,当有客户端发来连接请求时,启动接收消息的线程并进入发送消息的循环中 */ void createServer() { SOCKET server_listenSocket; //服务端的监听套接字,socket()创建的,监听客户端是否发来连接请求 SOCKET server_communiSocket; //服务端的通信套接字,accept()返回的,与客户端进行通信 struct sockaddr_in server_sockAddr; //包含服务端的本地接口和端口号的sockaddr_in结构体 struct sockaddr_in client_sockAddr; //包含所连接客服端的接口和端口号的sockaddr_in结构体 struct hostent *localHost; //包含本地主机的主机名和地址信息的hostent结构体指针 int iPort=4600; //设定为固定端口 char* localIP; //本地主机的IP地址 DWORD nThreadId = 0; //进程ID int iBindResult=-1; //绑定结果 int ires;//发送的返回值 int iWhileCount_bind = 10; //能够重新输入端口号绑定本地主机的机会次数 int iWhileCount_listen = 10; //能够重新监听的机会次数 char cWelcomBuffer[]="Welcome to you\0"; //欢迎消息的字符串 char cSendBuffer[1024];//发送消息缓存 char cShowBuffer[1024];//接收消息缓存 int len=sizeof(struct sockaddr); server_fp= fopen("MessageServer.txt","a");//打开记录消息的文件 //创建一个服务端的本地连接套接字 server_listenSocket = socket (AF_INET,SOCK_STREAM,0); //TCP方式,故type选择SOCK_STREAM流式套接字 printf("请输入本机绑定的端口号(大于1024):"); scanf("%d",&iPort); //获取本地主机的IP地址 localHost = gethostbyname(""); //获取包含本地主机的主机名和地址信息的hostent结构体指针 localIP = inet_ntoa (*(struct in_addr *)*localHost->h_addr_list); //获取本地主机的IP地址 //配置本地主机的网络地址信息 server_sockAddr.sin_family = AF_INET; //设置地址家族 server_sockAddr.sin_port = htons(iPort); //设置本地主机的端口号 server_sockAddr.sin_addr.S_un.S_addr = inet_addr(localIP); //设置本地主机的IP地址 //将套接字绑定在本地主机上 iBindResult=bind(server_listenSocket,(struct sockaddr*)&server_sockAddr,sizeof(struct sockaddr)); //如果端口不能被绑定,重新设置端口 while(iBindResult!=0 && iWhileCount_bind > 0) { printf("绑定失败,重新输入:"); scanf("%d",iPort); //配置本地主机的网络地址信息 server_sockAddr.sin_family = AF_INET; //设置地址家族 server_sockAddr.sin_port = htons(iPort); //设置本地主机的端口号 server_sockAddr.sin_addr.S_un.S_addr = inet_addr(localIP); //设置本地主机的IP地址 //将套接字绑定在本地主机上 iBindResult = bind(server_listenSocket,(struct sockaddr*)&server_sockAddr,sizeof(struct sockaddr)); iWhileCount_bind--; if(iWhileCount_bind<=0) { printf("端口绑定失败,重新运行程序\n"); exit(0); } } //重复监听 while(iWhileCount_listen>0) { printf("start listen\n"); listen(server_listenSocket,0);//返回值判断单个监听是否超时 server_communiSocket=accept(server_listenSocket,(struct sockaddr*)&client_sockAddr,&len); if(server_communiSocket!=INVALID_SOCKET) { //有连接成功,发送欢迎信息 send(server_communiSocket,cWelcomBuffer,sizeof(cWelcomBuffer),0); //启动接收消息的线程 CreateThread(NULL,0,threadproServer,(LPVOID)server_communiSocket,0,&nThreadId ); break; } printf("."); iWhileCount_listen--; if(iWhileCount_listen<=0) { printf("\n建立连接失败\n"); exit(0); } } while(1) { memset(cSendBuffer,0,1024); scanf("%s",cSendBuffer);//输入消息 if(strlen(cSendBuffer)>0)//输入消息不能为空 { ires = send(server_communiSocket,cSendBuffer,strlen(cSendBuffer),0);//发送消息 if(ires<0) { printf("发送失败"); } else { sprintf(cShowBuffer,"Send to : %s\n",cSendBuffer); printf("%s",cShowBuffer); fwrite(cShowBuffer ,sizeof(char),strlen(cShowBuffer),server_fp);//将消息写入日志 } if(strcmp("exit",cSendBuffer)==0) { ExitSystem(); } } } } /* 函数功能:创建点对点客户端 详细介绍:在客服端,输入服务端主机的IP地址,向服务端发送连接请求 */ void createClient() { SOCKET m_SockClient; struct sockaddr_in clientaddr; //包含客户端的本地接口和端口号的sockaddr_in结构体 char cServerIP[128]; //服务端的输入IP地址数组 int iWhileIP=10; //循环次数 int iCnnRes; //连接结果 DWORD nThreadId = 0; //线程ID值 char cSendBuffer[1024]; //发送消息缓存 char cShowBuffer[1024]; //显示消息缓存 char cRecvBuffer[1024]; //接收消息缓存 int recCharNum; //接收的字符个数 int ires; //发送消息的结果 int iIPRes; //检测IP是否正确 m_SockClient = socket ( AF_INET,SOCK_STREAM, 0 ); printf("请输入服务器地址:"); scanf("%s",cServerIP); //IP地址判断 if(strlen(cServerIP)==0) strcpy(cServerIP,"127.0.0.1");//没有输入地址,使用回环地址 else { iIPRes=CheckIP(cServerIP); while(!iIPRes && iWhileIP>0) { printf("请重新输入服务器地址:\n"); scanf("%s",cServerIP);//重新输入IP地址 iIPRes=CheckIP(cServerIP);//检测IP的合法性 iWhileIP--; if(iWhileIP<=0) { printf("输入次数过多\n"); exit(0); } } } client_fp= fopen("MessageServerClient.txt","a");//打开记录消息的文件 clientaddr.sin_family = AF_INET; //客户端向服务端请求的端口好,应该和服务端绑定的一致 clientaddr.sin_port = htons(4600); clientaddr.sin_addr.S_un.S_addr = inet_addr(cServerIP); iCnnRes = connect(m_SockClient,(struct sockaddr*)&clientaddr,sizeof(struct sockaddr)); if(iCnnRes==0)//连接成功 { recCharNum = recv(m_SockClient,cRecvBuffer,1024,0);//接收消息 if( recCharNum > 0 ) { printf("Receive form server : %s\n",cRecvBuffer); //启动接收消息的线程 CreateThread(NULL,0,threadproClient,(LPVOID)m_SockClient,0,&nThreadId ); } while(1) { memset(cSendBuffer,0,1024); scanf("%s",cSendBuffer); if(strlen(cSendBuffer)>0) { ires=send(m_SockClient,cSendBuffer,strlen(cSendBuffer),0); if(ires<0) { printf("发送失败\n"); } else { sprintf(cShowBuffer,"Send to : %s\n",cSendBuffer);//整理要显示的字符串 printf("%s",cShowBuffer); fwrite(cShowBuffer ,sizeof(char),strlen(cShowBuffer),client_fp);//记录发送消息 fflush(client_fp); } if(strcmp("exit",cSendBuffer)==0) { ExitSystem(); } } } }//iCnnRes else { // printf("%s",inet_addr(cServerIP)); printf("连接不正确\n"); } }
serverTranModule.h
#ifndef __pointToPointModule_H__ #define __pointToPointModule_H__ #include "stdafx.h" #include "common.h" extern void createServer(); //创建点对点服务端 extern void createClient(); //创建点对点客户端 #endif
serverTranModule.cpp [服务器中转模块]
#include "stdafx.h" #include <WinSock2.h> //包含socket套接字的API函数 #include "common.h" #include "serverTranModule.h" /* 函数功能:服务器中转模块的退出系统 详细介绍:服务器中转模块的退出系统与点对点模块有所不同,后者需要关闭文件,前者不需要 */ void ExitTranSystem() { WSACleanup(); exit(0); } /* 函数功能:负责中转服务端,用于中转消息和发送在线用户列表的线程 详细介绍: */ DWORD WINAPI threadTranServer(LPVOID pParam) { SOCKET hsock=(SOCKET)pParam;//获取SOCKET句柄 SOCKET sTmp;//临时存放用户的SOCKET句柄 char cRecvBuffer[1024];//接收消息的缓存 int num=0;//发送的字符串 int m,j;//循环控制变量 //char cTmp[2];//临时存放用户ID int ires; struct CSendPackage sp;//发包 struct CReceivePackage *p; if(hsock!=INVALID_SOCKET) printf("start:%d\n",hsock); while(1) { num=recv(hsock,cRecvBuffer,1024,0);//接收发送过来的信息 if(num>=0) { p = (struct CReceivePackage*)cRecvBuffer; switch(p->iType) { case CLIENTSEND_TRAN://对消息进行中转 for(m=0;m<2;m++) { if(usrinfo[m].ID==p->iToID) { //组包 sTmp=usrinfo[m].sUserSocket; memset(&sp,0,sizeof(sp)); sp.iType=SERVERSEND_SHOWMSG; strcpy(sp.cBuffer,p->cBuffer); ires = send(sTmp,(char*)&sp,sizeof(sp),0);//发送内容 if(ires<0) printf("发送失败\n"); } } break; case CLIENTSEND_LIST://发送在线用户 memset(&sp,0,sizeof(sp)); for(j=0;j<2;j++) { if(usrinfo[j].ID!=p->iFromID && usrinfo[j].ID!=0) { sp.cBuffer[j]=usrinfo[j].ID; } } sp.iType=SERVERSEND_ONLINE; send(hsock,(char*)&sp,sizeof(sp),0); break; case CLIENTSEND_EXIT: printf("退出系统\n"); return 0;//结束线程 break; } } } return 0; } /* 函数功能:中转服务端通知所有客户端有新用户登陆的线程 详细介绍: */ DWORD WINAPI NotyifyProc(LPVOID pParam) { struct CSendPackage sp;//发送包 SOCKET sTemp;//连接用户的socket句柄 int *p;//接收主线程发送过来的ID值 int j;//循环控制变量 p=(int*)pParam;//新用户ID for(j=0;j<2;j++)//去除新登录的,已经连接的 { if(usrinfo[j].ID != (*p)) { sTemp=usrinfo[j].sUserSocket; sp.iType=SERVERSEND_NEWUSR;//新上线通知 sprintf(sp.cBuffer,"%d\n",(*p)); send(sTemp,(char*)&sp,sizeof(sp),0);//发送新用户上线通知 } } return 0; } /* 函数功能:创建创建服务器中转服务端 详细介绍: */ void createTranServer() { SOCKET server_listenSocket;//开始监听的SOCKET句柄 struct sockaddr_in server_sockAddr;//用于绑定的地址信息 struct sockaddr_in client_sockAddr;//接收到的连接的地址信息 int iRes;//获取绑定的结果 SOCKET m_Server;//已建立连接的SOCKET句柄 struct hostent* localHost;//主机环境指针 char* localIP;//本地IP地址 struct CSendPackage sp;//发送包 int iMaxConnect=20;//允许的最大连接个数 int iConnect=0;//建立连接的个数 DWORD nThreadId = 0;//获取线程的ID值 char cWarnBuffer[]="It is voer Max connect\0";//警告字符串 int len=sizeof(struct sockaddr); int id;//新分配的客户ID localHost = gethostbyname(""); localIP = inet_ntoa (*(struct in_addr *)*localHost->h_addr_list);//获取本地IP server_sockAddr.sin_family = AF_INET; server_sockAddr.sin_port = htons(4600);//设置绑定的端口号 server_sockAddr.sin_addr.S_un.S_addr = inet_addr(localIP);//设置本地IP //创建套接字 server_listenSocket = socket (AF_INET,SOCK_STREAM,0); if(server_listenSocket == INVALID_SOCKET) { printf("建立套接字失败\n"); exit(0); } //绑定本地IP地址 iRes=bind(server_listenSocket,(struct sockaddr*)&server_sockAddr,sizeof(struct sockaddr)); if(iRes < 0) { printf("建立套接字失败\n"); exit(0); } //程序主循环 while(1) { listen(server_listenSocket,0);//开始监听 m_Server=accept(server_listenSocket,(struct sockaddr*)&client_sockAddr,&len);//接收连接 if(m_Server != INVALID_SOCKET) { printf("有新用户登录");//对方已登录 if(iConnect < iMaxConnect) { //启动接收消息线程 CreateThread(NULL,0,threadTranServer,(LPVOID)m_Server,0,&nThreadId ); //构建连接用户的信息 usrinfo[iConnect].ID=iConnect+1;//存放用户ID usrinfo[iConnect].sUserSocket=m_Server; usrinfo[iConnect].iPort=0;//存放端口,扩展用 //构建发包信息 sp.iType=SERVERSEND_SELFID;//获取的ID值,返回信息 sp.iCurConn=iConnect;//在线个数 id=iConnect+1; sprintf(sp.cBuffer,"%d\0",id); send(m_Server,(char*)&sp,sizeof(sp),0);//发送客户端的ID值 //通知各个客户端 if(iConnect>0) CreateThread(NULL,0,NotyifyProc,(LPVOID)&id,0,&nThreadId ); iConnect++; } else send(m_Server,cWarnBuffer,sizeof(cWarnBuffer),0);//已超出最大连接数 } } WSACleanup(); } /* 函数功能:创建服务器中转客户端的线程 详细介绍: */ DWORD WINAPI threadTranClient(LPVOID pParam) { SOCKET hsock=(SOCKET)pParam; int i;//循环控制变量 char cRecvBuffer[2048];//接收消息的缓存 int num;//接收消息的字符数 //char cTmp[2];//临时存放在线用户ID struct CReceivePackage sp;//服务端的接收包是,客户端的发送包 struct CSendPackage *p;//服务端的发送包是,客户端的接收包 int iTemp;//临时存放接收到的ID值 while(1) { num = recv(hsock,cRecvBuffer,2048,0);//接收消息 if(num>=0) { p = (struct CSendPackage*)cRecvBuffer; if(p->iType==SERVERSEND_SELFID) { iMyself=atoi(p->cBuffer); sp.iType=CLIENTSEND_LIST;//请求在线人员列表 send(hsock,(char*)&sp,sizeof(sp),0); } if(p->iType==SERVERSEND_NEWUSR)//登录用户ID { iTemp = atoi(p->cBuffer); usr[iNew++].ID=iTemp;//iNew表示有多少个新用户登录 printf("有新用户登录,可以与其聊天\n"); bSend=1;//可以发送消息聊天 } if(p->iType==SERVERSEND_SHOWMSG)//显示接受的消息 { printf("rec:%s\n",p->cBuffer); } if(p->iType==SERVERSEND_ONLINE)//获取在线列表 { for(i=0;i<2;i++) { if(p->cBuffer[i]!=iMyself && p->cBuffer[i]!=0) { usr[iNew++].ID=p->cBuffer[i]; printf("有用户在线,可以与其聊天\n"); bSend=1;//可以发送消息聊天 } } if(!bSend) printf("在线列表为空\n"); } } } return 0; } /* 函数功能:创建服务器中转客户端 详细介绍: */ void createTranClient() { SOCKET m_SockClient;//建立连接的socket struct sockaddr_in clientaddr;//目标的地址信息 int iRes;//函数执行情况 char cSendBuffer[1024];//发送消息的缓存 DWORD nThreadId = 0;//保存线程的ID值 struct CReceivePackage sp;//发包结构 char IPBuffer[128]; printf("输入服务器IP地址\n"); scanf("%s",IPBuffer); clientaddr.sin_family = AF_INET; clientaddr.sin_port = htons(4600);//连接的端口号 clientaddr.sin_addr.S_un.S_addr = inet_addr(IPBuffer); m_SockClient = socket ( AF_INET,SOCK_STREAM, 0 );//创建socket //建立与服务端的连接 iRes = connect(m_SockClient,(struct sockaddr*)&clientaddr,sizeof(struct sockaddr)); if(iRes < 0) { printf("连接错误\n"); exit(0); } //启动接收消息的线程 CreateThread(NULL,0,threadTranClient,(LPVOID)m_SockClient,0,&nThreadId ); while(1)//接收到自己ID { memset(cSendBuffer,0,1024); scanf("%s",cSendBuffer);//输入发送内容 if(bSend) { if(sizeof(cSendBuffer)>0) { memset(&sp,0,sizeof(sp)); strcpy(sp.cBuffer,cSendBuffer); sp.iToID=usr[0].ID;//聊天对象是固定的 sp.iFromID=iMyself;//自己 sp.iType=CLIENTSEND_TRAN; send(m_SockClient,(char*)&sp,sizeof(sp),0);//发送消息 } if(strcmp("exit",cSendBuffer)==0) { memset(&sp,0,sizeof(sp)); strcpy(sp.cBuffer,"退出");//设置发送消息的文本内容 sp.iFromID=iMyself; sp.iType=CLIENTSEND_EXIT;//退出 send(m_SockClient,(char*)&sp,sizeof(sp),0);//发送消息 ExitTranSystem(); } } else printf("没有接收对象,发送失败\n"); Sleep(10); } }
networkCommuniSys.cpp
#include "stdafx.h" #include <WinSock2.h> //包含socket套接字的API函数 #include "common.h" #include "pointToPointModule.h" #include "serverTranModule.h" //主函数 int _tmain(int argc, _TCHAR* argv[]) { int iSel=0; WSADATA wsd; WSAStartup(MAKEWORD(2,2),&wsd); do { printf("选择程序类型:\n"); printf("点对点服务端: 1\n"); printf("点对点客户端: 2\n"); printf("服务器中转服务端: 3\n"); printf("服务器中转客户端: 4\n"); scanf("%d",&iSel); }while(iSel<0 || iSel >4); switch(iSel) { case 1: createServer(); //创建点对点服务端 break; case 2: createClient(); //创建点对点客户端 break; case 3: createTranServer(); //创建服务器中转服务端 break; case 4: createTranClient(); //创建服务器中转客户端 break; } printf("退出系统\n"); return 0; }
启动系统,根据提示菜单选择1,就可以创建点对点服务端,输入固定端口号4600(客户端连接服务器使用的端口),输入后进入监听状态,当连接上客服端后,点对点服务端发送消息"Im hostA"。
原文地址:https://www.cnblogs.com/linuxAndMcu/p/9787473.html
时间: 2024-10-12 03:58:05