对socket编程的理解

  最近看了看socket网络编程,对于我这种一点经验都没有的选手来说只能理解一点点吧。所以在此记录一下最近的收获。

  socket编程无非就那几个函数,对于服务端来说,主要的为socket(),bind(),listen(),accept(),close()。对于客户端来说主要为connect(),close()等。当然,我所说的只是针对tcp协议而言的。对于udp而言,就可以简单很多,服务端和客户端都建立socket并进行绑定,从而用sendto()和recvfrom()通信即可。

  以下直接上一个关于tcp协议的客户端和服务端的程序。

//此为服务端的程序
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <netdb.h>
//当SIGCHLD信号出现时则执行此函数
//当子进程停止时,SIGCHLD信号会送给父进程,默认是忽略该信号
void sig_handler(int signo) {
    pid_t pid;
    int stat;
//用WNOHANG参数如果没有任何已终止的进程,它仍会立即返回,而不是像wait那样永远等下去
//waitpid如果成功的话返回子进程的pid,如果没有子进程退出则返回0
    pid = waitpid(-1, &stat, WNOHANG);
    while(pid > 0) {
        printf("child process terminated (PID: %ld)\n", (long)getpid());
        pid = waitpid(-1, &stat, WNOHANG);
    }

return 0;
}

int main(int argc, char *argv[]) {
    int listen_fd;
    int com_fd;
    int ret;
    int i;
    static char recv_buffer[1024];
    int len;
    int port;
    pid_t pid;
/*
struct sockaddr {
    unsigned short sa_family;  //address family, AF_xxx
    char            sa_data[14]; //14 bytes of protocal address
};
struct sockaddr_in {
    short int            sin_family;  //address family, AF_INET
    unsigned short int sin_port;    //port number
    struct in_addr      sin_addr;    //internet address
    unsigned char      sin_zero[8]; //same sizeas struct sockaddr
};
struct in_addr {
    uint32_t s_addr;  //4 bytes
};
sockaddr是通用套接字地址,sockaddr_in是internet环境下套接字的地址形式,两者
结构一样,都为16个字节。
*/

struct sockaddr_in clt_addr;
    struct sockaddr_in srv_addr;
//服务器运行时要给出端口信息,该端口为监听端口
    if(argc != 2) {
        printf("Usage: %s port\n", argv[0]);
        return 1;
    }
//atoi为ascii to integer,字符串转换为整型
    port = atoi(argv[1]);
//设置处理信号函数
    if(signal(SIGCHLD, sig_handler) < 0) {
        perror("cannot set the signal");
        return 1;
    }
//创建套接字用于服务器的监听
    if((listen_fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
        perror("cannot creat listening socket");
        return 1;
    }
//将srv_addr全部置零,INADDR_ANY就是inet_addr("0.0.0.0"),表示不确定
//地址,也就是表示本机的所有IP
    memset(&srv_addr, 0, sizeof(srv_addr));
    srv_addr.sin_family = AF_INET;
    srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    srv_addr.sin_port = htons(port);

if((ret = bind(listen_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))) == -1) {
        perror("cannot bind server socket");
        close(listen_fd);
        return 1;
    }
//指定监听端口,连接5个客户端
    if((ret = listen(listen_fd, 5)) == -1) {
        perror("cannot listen the client connect request");
        close(listen_fd);
        return 1;
    }
//对每个连接来的客户端创建一个进程,单独与其进行通信
//首先调用read函数读取客户端发送来的信息
//将其转换成为大写后发送回客户端
//当输入@时,程序退出
//用fork()函数的原因是保持服务器能容许多个客户端的连接
    while(1) {
        len = sizeof(clt_addr);
        if((com_fd = accept(listen_fd, (struct sockaddr*)&clt_addr, &len)) < 0) {
//EINTR表示系统调用被信号中断
            if(errno == EINTR) {
                continue;
            }
            else {
                perror("cannot accept client connect request");
                close(listen_fd);
                return 1;
            }
        }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/144cc584406619692f9e27ee5a0a01a3.html