一 问题
1 打开一个客户端不停地发包,资源监视器的网络IO 显示 1M/s的流量,再打开一个客户端,还是1M/s的流量,而且其中一个客户端会停止发包,过一会又会发包。
回答: 因为 winsocket默认是阻塞的,accept() ,receive是阻塞函数,当receive没有收到 数据时,程序不会继续往下执行,当socket缓冲区的数据满了,会阻止客户端发送数据 ,会出现粘包。流量1M/s 是因为ui线程打印会消耗CPU。导致不能全力发送数据。
1 mysocket.h
const size_t IP_BUF_SIZE = 65;
#include "mythread.h"
class MySocket : public MyThread{
public:
SOCKET m_socket;
SOCKET m_con;
public:
MySocket();
~MySocket();
void init();
void mybind();
void mylisten();
SOCKET myaccept();
void mysend(char * ch);
void myrecv();
void myclose();
void run();
void myconnect();
};
2 mythread.h
const size_t IP_BUF_SIZE = 65;
#include "mythread.h"
class MySocket : public MyThread{
public:
SOCKET m_socket;
SOCKET m_con;
public:
MySocket();
~MySocket();
void init();
void mybind();
void mylisten();
SOCKET myaccept();
void mysend(char * ch);
void myrecv();
void myclose();
void run();
void myconnect();
};
3 client.cpp
#include <windows.h>
#include <iostream>
#include "mysocket.h"
enum CMD{
CMD_LOGIN,
CMD_LOGOUT
} ;
struct HeaderData{
int datalegth;
int cmd;
};
struct LoginData{
HeaderData headerdata;
char username[100];
int password;
};
void main(){
LoginData logindata;
logindata.headerdata.datalegth =104;
logindata.headerdata.cmd = CMD_LOGIN;
MySocket * mysocket= new MySocket();
mysocket->init();
mysocket->myconnect();
int i =0;
//std::cout<<"请输入"<<std::endl;
while(1){
//printf("请输入用户名");
//scanf("%s",logindata.username);
//printf("请输入密码");
//scanf("%s",logindata.password);
strcpy(logindata.username,"xiongwei");
logindata.password = i;
printf("已输入用户名:%s 密码:%d",logindata.username,logindata.password);
//mysocket->mysend((char *)&logindata);
i=i+1;
// Sleep(5000);//每5秒
}
system("pause");
}
4 mymain.cpp
#include <windows.h>
#include <process.h>
#include "mysocket.h"
void receive(void * socket){
MySocket * mysocket ;
mysocket =(MySocket *)socket;
mysocket->myrecv();
}
void main321(){
MySocket * mysocket= new MySocket();
MySocket * clientsocket = new MySocket();
mysocket->mybind();
mysocket->mylisten();
while(1){
clientsocket->m_socket = mysocket->myaccept();
clientsocket->Start();
//clientsocket->run();
// _beginthread( receive, 0, clientsocket );
// mysocket->myrecv(socket);
clientsocket->myrecv();
}
mysocket->myclose();
system("pause");
}
5.mysocket.cpp
#include <Winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <iostream>
#include "mysocket.h"
#pragma comment(lib, "ws2_32.lib") //socket编程需要引用该库
struct LoginData{
char username[100];
int password;
};
MySocket::MySocket(){
this->init();
}
MySocket::~MySocket(){
}
void MySocket::init(){
WSADATA wsa_data; //WSADATA变量,包含windows socket执行的信息
int sys_fun_result = 0;
// 初始化winsock动态库(ws2_32.dll),MAKEWORD(2, 2)用于请求使用winsock2.2版本
sys_fun_result = WSAStartup(MAKEWORD(2, 2), &wsa_data);
if (sys_fun_result != 0) {
std::cout << "WSAStartup() function failed: " << sys_fun_result << "\n";
system("pause");
return ;
}
m_socket=socket(PF_INET,SOCK_STREAM,0);//创建套接字
std::cout<<"Winsock初始化成功"<<std::endl;
}
void MySocket::mybind(){
int sys_fun_result = 0;
SOCKADDR_IN addr;
addr.sin_family=AF_INET;
//addr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//ip地址
addr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
addr.sin_port=htons(16001);//绑定端口
bind(m_socket,(SOCKADDR*)&addr,sizeof(SOCKADDR));//绑定
if (sys_fun_result == SOCKET_ERROR) {
std::cout << "bind() function failed with error: " << WSAGetLastError() << "\n";
closesocket(m_socket);
WSACleanup();
return ;
}
}
void MySocket::mylisten(){
int sys_fun_result = 0;
sys_fun_result = listen(m_socket, SOMAXCONN);// SOMAXCONN 默认值 5 代表能够接收的最多的连接
if (sys_fun_result == SOCKET_ERROR) {
std::cout << "listen() function failed with error: " << WSAGetLastError() << "\n";
closesocket(m_socket);
return ;
}
}
SOCKET MySocket::myaccept(){
SOCKET socket;
char ip_buf[IP_BUF_SIZE];
SecureZeroMemory(ip_buf, IP_BUF_SIZE);
SOCKADDR_IN clientsocket;
int len=sizeof(SOCKADDR);
socket=accept(m_socket,(SOCKADDR*)&clientsocket,&len);
if (socket == INVALID_SOCKET) {
std::cout << "accept() function failed with error: " << WSAGetLastError() << "\n";
closesocket(m_socket);
WSACleanup();
system("pause");
return socket;
}
//ip地址转换
inet_ntop(AF_INET, &clientsocket, ip_buf, IP_BUF_SIZE);
std::cout << "client ip address: " << ip_buf << std::endl;
return socket;
}
void MySocket::mysend( char * ch){
int sys_fun_result = 0;
char * sendBuf = ch;
// memset(sendBuf,0,sizeof(sendBuf));
;
sys_fun_result= send(m_socket,sendBuf,208,0);
if(sys_fun_result==SOCKET_ERROR){
std::cout << "mysend() function failed with error: " << WSAGetLastError() << "\n";
}
std::cout<<"sys_fun_result ="<<sys_fun_result<<std::endl;
std::cout<<"strlen(sendBuf) ="<<strlen(sendBuf)<<std::endl;
std::cout<<"sizeof(sendBuf)="<<sizeof(sendBuf)<<std::endl;
std::cout<<sendBuf;
}
void MySocket::myrecv(){
char receiveBuf[208];
int sys_fun_result = 0;
while(1){
memset(receiveBuf,0,sizeof(receiveBuf));
/* 当recv返回值小于0时,socket报错;
当recv返回值大于0时,成功,返回值为接收到的数据长度;
当recv返回值等于0时,表示此时connect已经关闭,没有接收到数据。*/
sys_fun_result=recv(m_socket,receiveBuf,sizeof(receiveBuf),0);//这里不能用 strlen(receiveBuf) 因为 strlen(receiveBuf)=0
if (sys_fun_result == SOCKET_ERROR)
{
std::cout<<"error with code = "<<WSAGetLastError()<<std::endl;
//exit(1);
}
//Sleep(5000);
printf("clientsockid = %d,datalength =%d,cmd=%d,username=%s,password=%d\n", m_socket,*((int*)(&receiveBuf[0])),*( (int *) (&receiveBuf[4]) ),&receiveBuf[8],*((int*)(&receiveBuf[108])) );
}
}
void MySocket::myclose(){
closesocket(m_con);//关闭
WSACleanup();//释放资源的操作
}
void MySocket::myconnect(){
int sys_fun_result = 0;
SOCKADDR_IN clientsock_in;
clientsock_in.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
clientsock_in.sin_family=AF_INET;
clientsock_in.sin_port=htons(16001);
sys_fun_result = connect(m_socket,(SOCKADDR*)&clientsock_in,sizeof(SOCKADDR));
if (sys_fun_result == SOCKET_ERROR) {
std::cout << "connect() function failed with error: " << WSAGetLastError() << "\n";
WSACleanup();
system("pause");
return ;
}
}
void MySocket::run(){
std::cout<<"mysocket"<<std::endl;
//Sleep(5000);
this->myrecv();
}
6 mythread.cpp
#include<process.h>
#include<iostream>
#include "mythread.h"
void MyThread::Start( ){
_beginthread( Callback , 0, this );
};
void MyThread::Callback(void * p){
MyThread * obj = (MyThread *) p;
obj->run();
}