实现了C++下的TCP socket通信。由于不知道发送/接收信息的长度,因此,实现中先发送消息的长度,然后再发送消息内容;接收也一样,先接收消息长度,然后再接收消息的具体内容。实现代码在Linux下测试通过。
原来写的程序是有问题的,当发送的报文太长时,接收到的报文与发送的报文并不是一致的(接收到的报文不全)。找了一些资料发现“如果是阻塞的:man中的说明:
It is not an error if this number is smaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of-file, or because we are reading from a pipe, or from a terminal), or because read() was interrupted by a signal.
如果是非阻塞,因为可能读的时候所有的数据还没有接收完,read只返回当前socket缓冲中有的数据。”(http://bbs.byr.cn/#!article/Linux/106207)
修改后的server端程序如下。
server端的代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>
using namespace std;
void error(const string msg)
{
perror(msg.c_str());
exit(1);
}
void send_msg(int &sockfd, const string message)
{
int n = 0;
int length = message.length();
n = write(sockfd, (char *) &length, sizeof(length));
if(n < 0)
{
error("Error: cannot send message length to server!\n");
}
n = write(sockfd, message.c_str(), length);
if(n < 0)
{
error("Error: cannot send message to server!\n");
}
}
void recv_msg(int &sockfd)
{
int n = 0;
// fisrt accept the length of message
int length = 0;
n = read(sockfd, (char *) &length, sizeof(length));
cout << "MSG Length: " << length << endl;
if(n < 0)
{
error("Error: cannot read message length from client!\n");
}
// receive message from client
int buffer_length = length + 1;
char * buffer = new char[buffer_length];
bzero(buffer, buffer_length);
int byte_read = 0;
while(byte_read < length)
{
n = read(sockfd, buffer + byte_read, length - byte_read);
if(n < 0)
{
error("Error: cannot read message from client!\n");
}
byte_read += n;
}
cout << "MSG: " << buffer << endl;
}
int main(int argc, char* argv[])
{
int sockfd, newsockfd, portno;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
if(argc < 2)
{
error("Error: no port provided!\n");
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
error("Error: cannot open socket!\n");
}
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
{
error("Error: binding error!\n");
}
listen(sockfd, 5); // backlog = 5, defines the maximum length to which the queue of pending connections for sockfd may grow
clilen = sizeof(cli_addr);
while(1)
{
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if(newsockfd < 0)
{
error("Error: cannot accept!\n");
}
// recieve message
cout << "receiving message...\n";
recv_msg(newsockfd);
cout << "******************************\n";
// send message to client
cout << "sending message...\n";
send_msg(newsockfd, "Successfully received!");
close(newsockfd);
}
close(sockfd);
return 0;
}
client端的代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <iostream>
using namespace std;
void error(const string msg)
{
perror(msg.c_str());
exit(1);
}
void send_msg(int &sockfd, const string message)
{
int n = 0;
int length = message.length();
n = write(sockfd, (char *) &length, sizeof(length));
if(n < 0)
{
error("Error: cannot send message length to server!\n");
}
n = write(sockfd, message.c_str(), length);
if(n < 0)
{
error("Error: cannot send message to server!\n");
}
}
void recv_msg(int &sockfd)
{
int n = 0;
// fisrt accept the length of message
int length = 0;
n = read(sockfd, (char *) &length, sizeof(length));
cout << "MSG Length: " << length << endl;
if(n < 0)
{
error("Error: cannot read message length from client!\n");
}
// receive message from client
int buffer_length = length + 1;
char * buffer = new char[buffer_length];
bzero(buffer, buffer_length);
int byte_read = 0;
while(byte_read < length)
{
n = read(sockfd, buffer + byte_read, length - byte_read);
if(n < 0)
{
error("Error: cannot read message from client!\n");
}
byte_read += n;
}
cout << "MSG: " << buffer << endl;
}
int main(int argc, char * argv[])
{
int sockfd, portno;
struct sockaddr_in serv_addr;
struct hostent *server;
// char buffer[256];
if(argc < 3)
{
error("Error! Usage: cmd hostname port\n");
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
error("Error: cannot open socket!\n");
}
server = gethostbyname(argv[1]);
if(server == NULL)
{
error("Error: cannot find the server!");
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char *) &serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(portno);
if(connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
{
error("Error: cannot connect to server!\n");
}
cout << "Please enter the message: \n";
string message = "";
getline(cin, message);
// send message
cout << "sending message...\n";
send_msg(sockfd, message);
// receive message from server
cout << "receiving message...\n";
recv_msg(sockfd);
cout << "******************************\n";
close(sockfd);
return 0;
}
执行结果如下图所示:
参考文献:
http://www.linuxhowtos.org/C_C++/socket.htm
http://stackoverflow.com/questions/666601/what-is-the-correct-way-of-reading-from-a-tcp-socket-in-c-c
http://bbs.byr.cn/#!article/Linux/106207