本文简要介绍并实现一个客户端和服务器之间进行通信的程序,主要目的是为了了解建立客户端和服务器连接的过程,熟悉相应的API。
主要用到的API有如下几个,socket()、connect()、bind()、listen()、accept()。
下面附一个简单的代码实现:
首先,是客户端程序:
#include <sys/socket.h>
#include <netinet/in.h>
#include <memory.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
using namespace std;
int RunClient(int ServerPort, const char *strServerIP)
{
//根据指定的协议和套接字类型来获取客户端套接字
int ClientSocket = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == ClientSocket) //-1表示获取失败,返回并输出错误原因
{
cout << "get clientsocket error" << endl;
return -1;
}
//创建链接服务器所需要的信息
sockaddr_in ServerAddress; //定义一个套接口的地址对象,其本身是一个结构体,存贮服务器的端口、协议、地址长度、ip等信息
memset(&ServerAddress, 0, sizeof(sockaddr_in)); //将该地址对象所占用的内存空间清零
ServerAddress.sin_family = AF_INET; //对套接口地址对象中的字段赋值,协议与socket中一致
ServerAddress.sin_port = htons(ServerPort); //对端口字段进行赋值
int writeip = inet_pton(AF_INET, strServerIP, &ServerAddress.sin_addr); // 将服务器的ip地址存储到地址字段中
if(writeip != 1) //不等于一则说明存贮失败,打印错误信息,关闭客户端套接字并返回
{
cout << "inet_pton store ip error" << endl;
close(ClientSocket);
return -1;
}
cout << "prepare to connect server....." << endl;
//创建本地客户端套接字与网络服务器的链接
int conn = connect(ClientSocket, (sockaddr*)&ServerAddress, sizeof(ServerAddress));
if(conn == -1) //返回-1表示链接失败,关闭客户端套接字并返回错误信息
{
cout << "connect error" << endl;
close(ClientSocket);
return -1;
}
else
{
cout << "connect server success! " << endl;
}
//链接成功后,则读取从服务器返回的信息,并存入缓冲区中
char buf[13];
read(ClientSocket, buf, 11);
cout << "the info from server is:"<< endl;
cout << buf << endl; //打印服务器返回的信息
//完成信息传输后,关闭客户端套接字
close(ClientSocket);
cout << "client closed" << endl;
return 0;
}
int main()
{
RunClient(4000, "127.0.0.1");
return 0;
}
接着是服务器端程序:
#include <netinet/in.h>
#include <arpa/inet.h>
#include <memory.h>
#include <unistd.h>
#include <iostream>
using namespace std;
int RunServer(int Port, int LengthOfQueueOfListen = 100, const char *strBoundIP = NULL)
{
//创建一个用来监听的套接口
int ListenSocket = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == ListenSocket) //失败返回-1
{
cout << " creat listensocket error" << endl;
return -1;
}
//设置监听套接口的地址配置信息
sockaddr_in ServerAddress; //定义一个套接口的地址对象
memset(&ServerAddress, 0, sizeof(sockaddr_in)); //该对象所占用内存区域清零
ServerAddress.sin_family = AF_INET; //协议字段赋值
ServerAddress.sin_port = htons(Port); //端口
if(NULL == strBoundIP) //如果没有指定服务器绑定的ip,则监听本机的所有ip (多网卡时可能有多个ip)
{
ServerAddress.sin_addr.s_addr = htonl(INADDR_ANY);
}
else
{ //否则就使用指定的ip,将其写入到套接口的ip字段
int boundip = inet_pton(AF_INET, strBoundIP, &ServerAddress.sin_addr);
if(boundip != 1) //写入失败则返回错误,并关闭监听套接口
{
cout << "inet_pton error" << endl;
close(ListenSocket);
return -1;
}
}
//将监听套接口与其套接口地址信息绑定
int bindd = bind(ListenSocket, (sockaddr *)&ServerAddress, sizeof(sockaddr_in));
if(bindd == -1) //绑定失败返回错误信息,关闭监听套接口
{
cout << "bind error" << endl;
close(ListenSocket);
return -1;
}
//listen将监听套接口从主动转为被动状态,指示内核应该接收指向该套接口的链接请求,第二个参数表示最大连接个数
int lis = listen(ListenSocket, LengthOfQueueOfListen);
if(lis == -1) //监听失败返回错误信息,关闭监听套接字
{
cout << "listen error" << endl;
close(ListenSocket);
return -1;
}
//从连接的队列中,取出一个已完成的连接,并保存这个连接对应的客户端信息(ip、端口、协议等)
sockaddr_in ClientAddress; //创建套接口对象,主要用于存贮客户端的套接字信息
socklen_t LengthOfClientAddress = sizeof(sockaddr_in);
cout << "prepare to connect client....." << endl;
//进行连接,并存贮连接到的客户端套接口信息,若连接成功则会返回一个表示已经连接的套接口,用来进行信息传输
int ConnectedSocket = accept(ListenSocket, (sockaddr *)&ClientAddress, &LengthOfClientAddress);
if(-1 == ConnectedSocket) //若连接失败,则返回失败信息,关闭监听套接字
{
cout << "accept error" << endl;
close(ListenSocket);
return -1;
}
else
{
cout << "connect client success!" << endl;
}
//根据表示已经连接的套接口,向客户端发送信息
write(ConnectedSocket, "Hello World", 11);
cout << "the info return to client is:\r\nHello World"<< endl;
//关闭表示已经连接的套接口以及监听套接口
close(ConnectedSocket);
close(ListenSocket);
cout << "server closed \r\n";
return 0;
}
int main()
{
RunServer(4000);
return 0;
}
编译运行之后,得到的结果如下所示: