linux 下socket编程

原理

  1. 类unix系统中, 一切皆文件, 诸如磁盘文件, 显卡, 内核驱动, 网络协议栈等
  2. socket就是linux中提供的用于网络通信的文件接口, 两台机器之间可以读写消息
  3. 在使用socket真正的通信之前, 需要先建立连接, 连接的建立根据协议的不同, 建立的过程也不一样, 目前支持tcp, udp 协议, 通过raw socket, 也可以直接访问网络层的数据包, 更多可参考 http://man7.org/linux/man-pages/man2/socket.2.html

基本使用

功能

基于tcp, 实现一个echo 服务器, 回复客户端后, 直接关闭连接

测试

编译后, 先运行服务端(要有root权限); 用nc或者telnet, 连接监听的端口(示例中使用的是90) , 发送一条消息

示例

#include<stdio.h>   //定义了标准库的文件流操作的函数, 也定义了如printf, perror等
#include<stdlib.h>  //定义了很多辅助性的函数, 其中包括exit
#include<unistd.h>  //定义了文件操作的函数, 如close
#include<sys/types.h>  //定义了一些u_short, u_int, pid_t之类的数据类型, 为了保持和旧系统的兼容性, socket编程非必要, 参见man socket, NOTE段 
#include<sys/socket.h> //定义bind, listen, accept等函数 
#include<netinet/in.h> //定义了ip地址的结构体
#include<arpa/inet.h> //定义了ip地址转换的函数, 如inet_pton, inet_ntop
#include<errno.h>


void err_exit(const char* msg){
    int old_errno = errno;
    perror(msg);
    exit(old_errno);
}


int main(void){
    //从系统中申请socket句柄, 占用一个进程的文件描述符
    int serv_sock_fd = socket(AF_INET, SOCK_STREAM, 0);

    int on = 1;
    // 避免客户端和服务端在同台机器上, 如果先没有正常关闭socket, 导致的address already in use 的错误, 非必须
    setsockopt(serv_sock_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

        //定义连接需要的结构体, 里面会声明连接将要使用的协议, 支持AF_INET(ipv4), AF_INET6(ipv6), AF_UNIX(unix域套接字)等
        //结构体要置零, 避免未知的错误
    struct sockaddr_in serv_addr = {0};

    serv_addr.sin_family = AF_INET;
    //设置ip地址和端口时, 需要将值转换为网络字节序
    inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);
    serv_addr.sin_port = htons(90);

    //先要将socket和对应的连接协议信息进行绑定
    if(bind(serv_sock_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0){
        err_exit("socket bind failed");
    }

    //然后开始监听, 协议指定的地址和端口
    // listen的第二个参数, 请参考man 2 listen; 暂时设置为5
    // listen成功返回之后, 客户端就可以进行连接了
    if(listen(serv_sock_fd, 5) < 0){
        err_exit("socket listen failed");
    }

    int clnt_sock_fd = -1;
    char buffer[1024] = {0};
    while(1){
        //通过accept调用来获取已连接的客户端; 如果没有连接, 则阻塞,直到有连接进来
        clnt_sock_fd = accept(serv_sock_fd, 0, 0);
        if (clnt_sock_fd < 0){
            err_exit("accept failed");
        }

        //客户端成功连接后, 就可以在socket上, 进行读写(也可以称作发送和接收)了, 可以使用write/read方法, 也可以使用send/recv(请参看man 2 send)
        recv(clnt_sock_fd, buffer, sizeof(buffer), 0);
        send(clnt_sock_fd, buffer, sizeof(buffer), 0); 

        //完成响应后, 直接关闭客户端的连接
        close(clnt_sock_fd);
    }
}
posted @ 2017-09-06 00:43  哲淡  阅读(132)  评论(0编辑  收藏  举报