网络编程学习

SOCKET编程基础

服务器端

  1. 创建套接字:
1
2
3
4
5
6
int listenfd;

if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    printf("socket error!\n");
    exit(1);
}
  1. 设置服务器端口相关,这里为80端口,注意这里的sockaddr_in结构体。
1
2
3
4
5
6
struct sockaddr_in servaddr;

bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(80);
  1. 绑定端口。这里将sockaddr_in结构体转换为sockaddr结构体。
1
2
3
4
if( bind(listenfd, (struct sockaddr * ) &servaddr, sizeof(servaddr)) < 0) {
    printf("bind error!\n");
    exit(1);
}

# 监听端口,这里设置套接字排队最大连接数为5,根据BSD传统。

1
2
3
4
if( listen(listenfd, 5) < 0) {
    printf("listen error!\n");
    exit(1);
}
  1. 处理连接。
1
2
3
4
5
6
7
8
9
10
11
12
13
int connfd;

for(;;) {
    if ((connfd = accept(listenfd, struct sockaddr * ) NULL, NULL) < 0) {
        printf("accept error!\n");
        exit(1);
    }
    /* 这里处理连接请求,最简单的是直接处理,但如果比较耗时的话其他就访问不了。
     * 要能够同时处理多个请求,一般用线程或子进程来完成,
     * 这样主进程能够继续或者说同时处理下一个请求。 \*/
    close(connfd);
}
close(listenfd);

客户端

  1. 创建套接字
1
2
3
4
5
6
int sockfd;
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0) < 0)) {
    printf("socket error.\n");
    exit(1);
    }
}
  1. 设置访问端口。
1
2
3
4
5
6
7
8
9
10
struct sockaddr_in servaddr;

bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(80);

if( inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr) <= 0) {
    printf("inet_pton error.\n");
    exit(1);
}
  1. 发起连接。错误代码保存在全局变量errno中,errno在errno.h中定义。
1
2
3
4
if( connect(sockfd, (struct sockaddr * ) &servaddr, sizeof(servaddr)) < 0) {
    printf("connect error: %s\n", strerror(errno));
    exit(1);
}
  1. 读写socket,应用处理。这里可以使用各种I/O复用等。
1
2
while(read(sockfd, recvlien, MAXLINE) > 0) {
}

I/O复用技术

select

头文件

1
#include <sys/select.h>

定义相关变量并初始化:

1
2
3
4
5
6
7
int maxfdp1;
fd_set rset;

FD_ZERO(&rset);
FD_SET(fileno(fp), &rset);
FD_SET(sockfd, &rset);
maxfdp1 = max(fileno(fp), sockfd) + 1;

使用select,在可读时响应:

1
2
3
4
if(( n=select(maxfdp1, &rset, NULL, NULL, NULL)) < 0) {
    printf("select error.\n");
    exit(1);
}

通过FD_ISSET()判断哪个文件描述符可读:

1
2
3
4
5
6
7
if( FD_ISSET(fileno(fp), &rset)) {
    // Now can read from fp.
}

if( FD_ISSET( sockfd, &rset)) {
    // Now can read from sockfd.
}

具体的例子参见str_cli2函数(在echo_client.c中)。

pselect

poll

相关库

  1. libevent