C++服务器(一):了解Linux下socket编程

来源:互联网 发布:无锡汽车模具编程招聘 编辑:程序博客网 时间:2024/05/21 02:34

最近想要用C++写个socket的服务器,用于日常的项目开发。
不过,我是新手,那就慢慢地学习一下吧。

首先,先写一段程序,用起来先。
感谢博文: Linux下 C++调用C 实现socket网络通讯编程 - 雕戈 - ITeye技术网站

Server

#include<iostream>using namespace std;//head files of Linux#include<netinet/in.h>#include<unistd.h>   //for fork and read#include<sys/types.h>   //for socket#include<sys/socket.h>  //for socket#include<string.h> // for bzero#include<arpa/inet.h>void server(){    const unsigned short SERVERPORT = 53556;    const int BACKLOG = 10; //10 个最大的连接数    const int MAXSIZE = 1024;    int sock, client_fd;    sockaddr_in myAddr;    sockaddr_in remoteAddr;    sock = socket(AF_INET, SOCK_STREAM, 0);    //create socket    if( sock == -1)    {        cerr<<"socket create fail!"<<endl;        exit(1);    }    cout<<"sock :"<<sock<<endl;    //bind    myAddr.sin_family = AF_INET;    myAddr.sin_port = htons(SERVERPORT);    myAddr.sin_addr.s_addr = INADDR_ANY;    bzero( &(myAddr.sin_zero), 8);    if(bind(sock, (sockaddr*)(&myAddr), sizeof(sockaddr)) ==-1 )    {        cerr<<"bind error!"<<endl;        exit(1);    }    //listen    if(listen(sock, BACKLOG) == -1)    {        cerr<<"listen error"<<endl;        exit(1);    }    while(true)    {        unsigned int sin_size = sizeof(sockaddr_in);        if( (client_fd = accept(sock, (sockaddr*)(&remoteAddr), &sin_size)) ==-1 )        {            cerr<<"accept error!"<<endl;            continue;        }        cout<<"Received a connection from "<<static_cast<char*>(inet_ntoa(remoteAddr.sin_addr) )<<endl;        //子线程        if(!fork() )        {            int rval;            char buf[MAXSIZE];            if( (rval = read(client_fd, buf, MAXSIZE) ) <0)            {                cout<<"Reading stream error!\n";                continue;            }            cout<<buf<<endl;            //向客户端发送信息            const char* msg = "Hello, I am xiaojian. You are connected !";            if( send(client_fd, const_cast<char*>(msg), strlen(msg), 0) == -1)                cerr<<"send error!"<<endl;            close(client_fd);            exit(0);        }    }}       int main(){    server();}

Client

#include<iostream>using namespace std;#include<string.h>#include<netinet/in.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>#include<unistd.h>void client(){    const unsigned short SERVERPORT = 53556;    const int MAXSIZE = 1024;    const char* SERVER_IP = "115.159.90.99";    const char* DATA = "this is a client message ";    int sock, recvBytes;    char buf[MAXSIZE];//    hostent *host;    sockaddr_in serv_addr;    if( (sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)    {        cerr<<"socket create fail!"<<endl;        exit(1);    }    bzero( &serv_addr, sizeof(serv_addr) );    serv_addr.sin_family =  AF_INET;    serv_addr.sin_port = htons(SERVERPORT);    serv_addr.sin_addr.s_addr = inet_addr(SERVER_IP);    if( connect(sock, (sockaddr*)&serv_addr, sizeof(sockaddr)) == -1)    {        cerr<<"connect error"<<endl;        exit(1);    }    write(sock, const_cast<char*>(DATA), strlen(DATA) );    if( (recvBytes = recv(sock, buf, MAXSIZE, 0)) == -1)    {        cerr<<"recv error!"<<endl;        exit(1);    }    buf[recvBytes] = '\0';    cout<<buf<<endl;    close(sock);}int main(){    client();}

代码比较容易理解,主要是各种 API 的理解和使用。

一些函数和结构

sockaddr_in

解释一下代码:
首先看到一个结构体:sockaddr_in,这是什么结构呢
sockaddr_in 在头文件in.h中声明,这个头文件在/usr/include/netinet/目录下,去一看究竟,可以找到它的声明:

/* Structure describing an Internet socket address.  */struct sockaddr_in  {    __SOCKADDR_COMMON (sin_);    in_port_t sin_port;         /* Port number.  */    struct in_addr sin_addr;        /* Internet address.  */    /* Pad to size of `struct sockaddr'.  */    unsigned char sin_zero[sizeof (struct sockaddr) -               __SOCKADDR_COMMON_SIZE -               sizeof (in_port_t) -               sizeof (struct in_addr)];  };

一个比较好的解释:sockaddr和sockaddr_in的区别 - The time is passing - ITeye技术网站
(其实我并不懂,所以暂时不解释)
但是这里要提到的几句代码:

    myAddr.sin_family = AF_INET;    myAddr.sin_port = htons(SERVERPORT);    myAddr.sin_addr.s_addr = INADDR_ANY;    bzero( &(myAddr.sin_zero), 8);

其中 AF_INET 定义了协议族(TCP\UDP等)。

socket函数

函数名: socket(建立一个socket通信)
表头文件: #include<sys/types.h> #include<sys/socket.h>
定义函数: int socket(int domain,int type,int protocol);
函数说明: socket()用来建立一个新的socket,也就是向系统注册,通知系统建立一通信端口。参数domain 指定使用何种的地址类型,完整的定义在/usr/include/bits/socket.h内。
返回值: 成功则返回socket处理代码,失败返回-1。

bind函数

函数名: bind(对socket定位)
表头文件: #include<sys/types.h> #include<sys/socket.h>
定义函数: int bind(int sockfd,struct sockaddr * my_addr,int addrlen);
函数说明: bind()用来设置给参数sockfd的socket一个名称。此名称由参数my_addr指向一sockaddr结构,对于不同的socket domain定义了一个通用的数据结构
返回值: 成功则返回0,失败返回-1,错误原因存于errno中。

listen函数

函数名: listen(等待连接)
表头文件: #include<sys/socket.h>
定义函数: int listen(int s,int backlog);
函数说明: listen()用来等待参数s 的socket连线。参数backlog指定同时能处理的最大连接要求,如果连接数目达此上限则client端将收到ECONNREFUSED的错误。Listen()并未开始接收连线,只是设置socket为listen模式,真正接收client端连线的是accept()。通常listen()会在socket(),bind()之后调用,接着才调用accept()。
返回值: 成功则返回0,失败返回-1,错误原因存于errno。
附加说明: listen()只适用SOCK_STREAM或SOCK_SEQPACKET的socket类型。如果socket为AF_INET则参数backlog 最大值可设至128。

accept函数

函数名: accept(接受socket连线)
表头文件: #include<sys/types.h> #include<sys/socket.h>
定义函数: int accept(int s,struct sockaddr * addr,int * addrlen);
函数说明: accept()用来接受参数s的socket连线。参数s的socket必需先经bind()、listen()函数处理过,当有连线进来时accept()会返回一个新的socket处理代码,往后的数据传送与读取就是经由新的socket处理,而原来参数s的socket能继续使用accept()来接受新的连线要求。连线成功时,参数addr所指的结构会被系统填入远程主机的地址数据,参数addrlen为scokaddr的结构长度。关于结构sockaddr的定义请参考bind()。
返回值: 成功则返回新的socket处理代码,失败返回-1,错误原因存于errno中。

send函数

函数名: send(经socket传送数据)
表头文件: #include<sys/types.h> #include<sys/socket.h>
定义函数: int send(int s,const void * msg,int len,unsigned int falgs);
函数说明: send()用来将数据由指定的socket 传给对方主机。参数s为已建立好连接的socket。参数msg指向欲连线的数据内容,参数len则为数据长度。参数flags一般设0。
返回值: 成功则返回实际传送出去的字符数,失败返回-1。错误原因存于errno

recv函数

函数名: recv(经socket接收数据)
表头文件: #include<sys/types.h> #include<sys/socket.h>
定义函数: int recv(int s,void *buf,int len,unsigned int flags);
函数说明: recv()用来接收远端主机经指定的socket传来的数据,并把数据存到由参数buf 指向的内存空间,参数len为可接收数据的最大长度。
返回值: 接收的实际长度

connect函数

函数名: connect(建立socket连线)
表头文件: #include<sys/types.h> #include<sys/socket.h>
定义函数: int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);
函数说明: connect()用来将参数sockfd 的socket 连至参数serv_addr 指定的网络地址。结构sockaddr请参考bind()。参数addrlen为sockaddr的结构长度。
返回值: 成功则返回0,失败返回-1,错误原因存于errno中。

其他函数

read : 由已打开的文件读取数据
write: 将数据写入已打开的文件内
htons:将16位主机字符顺序转换成网络字符顺序
bzero:将一段内存内容全清为零
inet_addr:将网络地址转成二进制的数字

函数可以在参考资料中的Linux 常用手册找到。
但为了代码的可兼容性,我个人的意见是,尽量少用依赖于平台的函数,多用标准库,这样代码可以轻易移植到其他支持 C++ 编译的平台。

参考资料:
Linux下 C++调用C 实现socket网络通讯编程 - 雕戈 - ITeye技术网站
sockaddr和sockaddr_in的区别 - The time is passing - ITeye技术网站
简单理解Socket - Samaritans - 博客园
Linux 常用C函数(中文版)

1 0