c++ - Echo server/client message cutoff using socket send()/recv()(winsock) -
the code echo_server , echo_client posted below. noticed when type message on length in echo_client, server truncates end of , echoes part of it. buffer size 1024 bytes , message i'm typing much, shorter length. what's going on here? how solve problem server echoes complete message provided under length limit?
the server code:
#include "stdafx.h" #include <iostream> #include <string> #include <string.h> #ifndef unicode #define unicode #endif #define win32_lean_and_mean #include <winsock2.h> #include <ws2tcpip.h> #pragma comment(lib, "ws2_32.lib") using namespace std; static int maxpending = 5; int main(int argc, char *argv[]) { wsadata wsadata; int iresult; int optv = 0; bool connected = false; char *optval = (char*)&optv; int optlen = sizeof(optval); string q = "quit"; const char *exit =q.c_str(); iresult = wsastartup(makeword(2, 2), &wsadata); if (iresult != no_error) { wprintf(l"wsastartup function failed error: %d\n", iresult); return 1; } if(argc!=2){ printf("error: incorrect number of arguments. "); return 1; } socket servsock; servsock = socket(af_inet,sock_stream,ipproto_tcp); if(servsock==invalid_socket){ printf("socket function failed error: %d\n",getlasterror()); return 1; } u_int servport = atoi(argv[1]); sockaddr_in servaddr; servaddr.sin_family = af_inet; servaddr.sin_addr.s_addr = htonl(inaddr_any); servaddr.sin_port = htons(servport); int opt = setsockopt(servsock,sol_socket,so_reuseaddr,optval,sizeof(optval)); if(bind(servsock,(sockaddr*)&servaddr,sizeof(servaddr))<0){ printf("bind function failed error: %d\n", getlasterror()); return 1; } for(;;){ if(listen(servsock,maxpending) < 0){ printf("listen function failed error: %d/n",getlasterror()); return 1; }else{ char *str = new char[5]; printf("server listening on port %d\n",servport); } socket clientsock; sockaddr_in clientaddr; socklen_t caddrlen = sizeof(clientaddr); clientsock = accept(servsock,(sockaddr*)&clientaddr,&caddrlen); if(clientsock < 0){ printf("accept() function failed error: %d/n", getlasterror()); goto quit; }else if(clientsock >=0){ connected = true; } char cname[inet_addrstrlen]; if(inet_ntop(af_inet,&clientaddr.sin_addr,cname,sizeof(cname))!=null){ printf("handling client %s/%d\n", cname,ntohs(clientaddr.sin_port)); }else{ printf("error: unable client address"); } char buffer[1024]; while(connected==true){ long nbytesrcvd = recv(clientsock,buffer,sizeof(buffer),0); if(nbytesrcvd==0){ connected = false; cout << endl; cout << cname << ": client disconnected" << endl; cout << endl; break; } if(nbytesrcvd < 0){ printf("error: recv() failed"); cout << endl; goto quit; } if(nbytesrcvd > 0){ long nbytessent = send(clientsock,buffer,nbytesrcvd,0); if(nbytessent < 0){ cout << "error: send() failed" << endl; cout << endl; goto quit; }else if(nbytessent!=nbytesrcvd){ cout << "send() error: sent unexpected # of bytes" << endl; cout << endl; goto quit; } } } quit: int iresult = closesocket(clientsock); if (iresult == socket_error) { printf("closesocket function failed error: %d\n",getlasterror()); } } }
and client code:
#include "stdafx.h" #include <iostream> #include <string> #include <string.h> #ifndef unicode #define unicode #endif #define win32_lean_and_mean #include <windows.h> #include <winsock2.h> #include <ws2tcpip.h> #define buffsize 1024 #pragma comment(lib, "ws2_32.lib") using namespace std; int main(int argc,char* argv[]) { wsadata wsadata; int result; bool connected = false; hostent *rhost; char buffer[buffsize]; string q = "quit"; const char *exit =q.c_str(); result = wsastartup(makeword(2, 2), &wsadata); if (result != no_error) { printf("wsastartup function failed error: %d\n", result); return 1; } socket connector; connector = socket(af_inet, sock_stream, ipproto_tcp); if (connector == invalid_socket) { wprintf(l"socket function failed error: %ld\n", wsagetlasterror()); wsacleanup(); return 1; } string hname; cout << "enter host name(url): "; cin >> hname; cout << endl; string portnum; cout << "enter port number wish connect on: " ; cin >> portnum; cout << endl; char *hostname = const_cast<char*>(hname.c_str()); char *hostport = const_cast<char*>(portnum.c_str()); rhost = gethostbyname(hostname); in_addr addr; addr.s_addr = *(u_long *)rhost->h_addr_list[0]; sockaddr_in clientserv; clientserv.sin_family = af_inet; clientserv.sin_addr.s_addr = addr.s_addr; clientserv.sin_port = htons(atoi(hostport)); if(connect(connector,(sockaddr*)&clientserv,sizeof(clientserv))==socket_error){ printf("connect function failed error: %d\n", getlasterror()); if(closesocket(connector)<0){ printf("closesocket function failed %d\n", getlasterror()); } return 1; }else{ connected = true; cout << "connected host " << hname << " on port " << portnum << endl; cout << "type 'quit' exit program " << endl; } while(connected==true){ int nbr = 0; string msg; cout << ">"; getline(cin,msg); cout << endl; if(msg=="quit"){ connected = false; goto quit; } int len = sizeof(msg); const char *message = msg.c_str(); strncpy_s(buffer,message,sizeof(msg)); long nbs = send(connector,buffer,len,0); if(nbs < 0){ printf("send() failed", getlasterror()); return 1; } while(nbr < nbs){ nbr = recv(connector,buffer,len,0); if(nbr < 0){ printf("recv() failed", getlasterror()); return 1; }else if(nbr==0){ printf("recv() failed: connection closed prematurely", getlasterror()); return 1; }else if(nbr > 0){ string str(buffer); cout << ">> " << str << endl; cout << endl; } } } quit: if (closesocket(connector) == socket_error) { printf("closesocket function failed error: %ld\n", getlasterror()); wsacleanup(); return 1; } wsacleanup(); return 0;
}
remember recv
may not receive of data @ once. means recv
call in server may not complete message, send gets , in turn client may receive part of sent server.
this not problem, unless receive once in client , continue doing else. do.
there 2 solutions this:
make simple protocol either prepends each message message length, or have special end-of-message marker. in first case know how data there , can read in loop until received, in second case receive in aloop until end-of-message marker.
make sockets non-blocking, , read in loop until
recv
returns error error beingwsaewouldblock
. there no more data read.
mistakes in client:
int len = sizeof(msg);
this line returns size of string object, not string contained inside object. should use msg.length()
length. use in couple of places.
using std::string
quite okay, if use properly. e.g.:
long nbs = send(connector, msg.c_str(), msg.length(), 0);
another thing:
nbr = recv(connector, buffer, len, 0);
you again use wrong size here, 1 provided , erroneously use of sizeof(msg)
. instead should provide size of actual buffer. since buffer
proper array can use e.g. sizeof(buffer) - 1
here. -1
because data send client not zero-terminated string, , need reserve space in received data terminate string:
buffer[nbr] = '\0';
Comments
Post a Comment