Linux网络编程——服务器客户端的代码实现

服务器

  1. 调用socket函数创建socket;
  2. 调用bind函数将socket绑定到某个IP和端口的二元组上;
  3. 调用listen函数开始监听
  4. 当有客户端请求连接上来时,调用accept函数接收连接,产生一个新的socket(客户端socket);
  5. 基于新产生的socket调用send或者recv函数,开始与客户端进行数据交流;
  6. 通信结束后,调用close函数关闭监听socket。

简单的服务器代码:

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>


int main(int argc, const char * argv[]) {
    // insert code here...
    //std::cout << "Hello, World!\n";
    //创建一个监听socket
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if(listenfd == -1){
        std::cout<<"create listen socket error"<<std::endl;
        return -1;
    }
    //初始化服务器地址
    struct sockaddr_in bindaddr;
    bindaddr.sin_family = AF_INET;
    bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    bindaddr.sin_port = htons(3000);
    if(bind(listenfd, (struct sockaddr*) &bindaddr, sizeof(bindaddr))){
        std::cout<<"bind listen socket error"<<std::endl;
        return -1;
    }
    //启动监听
    if(listen(listenfd, SOMAXCONN) == -1){
        std::cout<<"listen error"<<std::endl;
        return -1;
    }
    while(true){
        struct sockaddr_in clientaddr;
        socklen_t clientaddrlen = sizeof(clientaddr);
        //接收客户端连接
        int clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddrlen);
        if(clientfd != -1){
            char recvBuf[32] = {0};
            //从客户端接收数据
            int ret = recv(clientfd, recvBuf, 32, 0);
            if(ret > 0){
                std::cout<<"recv data from client, data:"<<std::endl;
                //将收到的数据原封不动的发给客户端
                ret = send(clientfd, recvBuf, strlen(recvBuf), 0);
                if(ret != strlen(recvBuf)){
                    std::cout<<"send data error"<<std::endl;
                }else{
                    std::cout<<"send data to client success"<<std::endl;
                }
            }else{
                std::cout<<"recv data error."<<std::endl;
            }
            close(clientfd);
        }
    }
    
    //关闭监听socket
    close(listenfd);
    return 0;
}

客户端

  1. 调用socket函数创建客户端socket;
  2. 调用connect函数尝试连接服务器;
  3. 连接成功后调用send或recv函数,开始与服务器进行数据交流;
  4. 通信结束后,调用close函数关闭监听socket。

简单的客户端代码:

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>

#define SERVER_ADDRESS "127.0.0.1"
#define SERVER_PORT 3000
#define SEND_DATA  "hello world"



int main(int argc, const char * argv[]) {
    // insert code here...
    //std::cout << "Hello, World!\n";
    //创建一个socket
    int clientfd = socket(AF_INET, SOCK_STREAM, 0);
    if(clientfd == -1){
        std::cout<<"create client socket error"<<std::endl;
        return -1;
    }
    //连接服务器
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);
    serveraddr.sin_port = htons(SERVER_PORT);
    if(connect(clientfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) == -1){
        std::cout<<"connect socket error"<<std::endl;
        return -1;
    }
    
    //向服务器发送消息
    int ret = send(clientfd, SEND_DATA, strlen(SEND_DATA), 0);
    if(ret != strlen(SEND_DATA)){
        std::cout<<"send data error"<<std::endl;
        return -1;
    }
    std::cout<<"send data successfully, data:"<<SEND_DATA<<std::endl;
    
    //从服务器收取数据
    char recvBuf[32] = {0};
    ret = recv(clientfd, recvBuf, 32, 0);
    if(ret < 0){
        std::cout<<"recv data successfully, data:"<<recvBuf<<std::endl;
    }else{
        std::cout<<"recv data error, data:"<<recvBuf<<std::endl;
    }
    //关闭socket
    close(clientfd);
    return 0;
}

bind函数分析

函数原型:

int bind(int, const struct sockaddr *, socklen_t)

结构体sockaddr:

struct sockaddr {
	__uint8_t       sa_len;         /* total length */
	sa_family_t     sa_family;      /* [XSI] address family */
	char            sa_data[14];    /* [XSI] addr value (actually larger) */
};

其中typedef __uint8_t sa_family_t;
结构体sockaddr_in:

struct sockaddr_in {
	__uint8_t       sin_len;
	sa_family_t     sin_family;
	in_port_t       sin_port;
	struct  in_addr sin_addr;
	char            sin_zero[8];
};

bind函数的用法:
由bind的函数原型可以知道,第一个参数为socket文件描述符,socket变量Linux系统中为int类型;第二个参数为sockaddr类型的指针,使用sockaddr_in时需要强制类型转换;第三个参数为sockaddr_in的长度
具体实例如服务器中所示,代码片段为:

struct sockaddr_in bindaddr;
bindaddr.sin_family = AF_INET;
bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bindaddr.sin_port = htons(3000);
if(bind(listenfd, (struct sockaddr*) &bindaddr, sizeof(bindaddr)) == -1){
	std::cout<<"bind error"<<std::endl;
	return -1;
}

如果应用程序不关心bind绑定的IP,可以使用INADDR_ANY,底层服务会自动选择一个合适的IP地址。

在TCP通信双方中,一般服务器端的端口号是固定的,而客户端的端口号是连接发起时由操作系统随机分配的。端口号是一个C short类型的值,其范围是0~65535。如果将bind函数中的端口号设置为0,那么操作系统会随机为程序分配一个可用的监听端口。但是,服务器一般不会这么做,因为必须要让客户端知道明确的IP地址和端口号。


版权声明:本文为qq_35696023原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。