Unix网络编程(一、简单回显服务器)

socket API

socket

1
int socket(int domain, int type, int protocol);

功能:打开一个socket
返回值:成功返回对应的文件描述符,否则为-1
参数:

  1. family指明了协议簇,通常AF_INET、AF_INET6、AF_LOCAL等;
  2. type是套接口类型,主要SOCK_STREAM、SOCK_DGRAM、SOCK_RAW;
  3. protocol一般取为0。

bind

1
int bind(int sockfd, const struct sockaddr* myaddr,socklen_t addrlen);

功能:将sockaddr绑定到对应的文件描述符上
返回值:成功0,失败-1
参数: 主要说一下sockaddr

1
2
3
4
5
6
7
8
9
10
11
12
13
struct sockaddr_in
{
sa_family_t sin_family; // 协议
in_port_t sin_port; // 端口号
struct in_addr sin_addr; // 网络地址
// ...
}

struct sockaddr
{
// ...
char sa_data[14]; // Address data.
};

通常我们通过 sockaddr_in 端口和ip后将起其转换成 sockaddr供函数使用
两种特殊情况:

  1. port可以设置为0 表示有系统自动分配
  2. sin_addr可以设置成通配地址 IN_ADDRANY 表示由内核去选择ip地址

listen

1
int listen(int sockfd, int backlog);

功能:仅由tcp服务端调用 它将一个未被连接的套接字转换成被动套接字,也就是说内核应该接受指向该套接字的连接请求,调用该函数会将套接字的状态从 close 态转变为 listen
参数: backlog表示准许连接的最大套接字数目。这个数目由已完成的连接队列和未完成的连接队列两部分组成。未完成队列可能是发送了SYN但三次握手还没完成的。
返回值:成功0,失败-1

accept

1
int accept(int sockfd, struct sockaddr *clientaddr, socklen_t *addrlen);

功能:由tcp服务端调用,它会从已完成队列的对队首返回一个已完成连接的文件描述符,如何已完成队列为空,会使进程休眠(默认使用阻塞套接字)
参数: sockfd为服务端监听的套接字
返回值: 由内核生成的已连接套接字

connect

1
int connect(int sockfd,conststruct sockaddr *servaddr, socklen_t addrlen)

功能:在tcp协议中,该函数用来发起三次握手,建立和服务端的连接。
返回值:0成功,-1失败

回显服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <asm-generic/errno-base.h>

static const int MAXLINE = 4096;
static const int MAXLISTEN = 1024;
static const int PORT = 45678;

int e_socket(int domain, int type, int protocol);
int e_bind(int sockfd, sockaddr_in* myaddr, socklen_t addrlen);
int e_listen(int sockfd, int backlog);
int e_accept(int sockfd, sockaddr_in* myaddr, socklen_t *addrlen);
void my_echo(int connectfd);

int e_socket(int domain, int type, int protocol){
int sockfd = socket(domain, type, protocol);
if (sockfd < 0)
{
exit(0);
}
return sockfd;
}

int e_bind(int sockfd, sockaddr_in* myaddr, socklen_t addrlen){
int ret = bind(sockfd, (sockaddr*)myaddr, addrlen);
if (ret != 0){
close(sockfd);
exit(0);
}
return ret;
}

int e_listen(int sockfd, int backlog){
int ret = listen(sockfd, backlog);
if (ret != 0) {
close(sockfd);
exit(0);
}
return ret;
}

int e_accept(int sockfd, sockaddr_in* myaddr, socklen_t *addrlen){
int ret = accept(sockfd, (sockaddr*)myaddr, addrlen);
if (ret == -1)
{
printf("%d", errno);
}
return ret;
}

void my_echo(int connectfd){
ssize_t n;
char buf[MAXLINE] = {0};
while ((n = read(connectfd, buf, MAXLINE)) > 0) {
write(connectfd, buf, n);
memset(buf, 0, MAXLINE);
if (n < 0 && errno == EINTR)
continue;
}
}

int main(int argc, const char * argv[]) {

int listen_sock = e_socket(AF_INET, SOCK_STREAM, 0);

sockaddr_in servaddr, clientaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(PORT);

e_bind(listen_sock, &servaddr, sizeof(servaddr));
e_listen(listen_sock, MAXLISTEN);

socklen_t clientlen = 0;
int clientfd = -1;
while (true) {
clientlen = sizeof(clientaddr);
clientfd = e_accept(listen_sock, &clientaddr, &clientlen);

if (fork() == 0) {
close(listen_sock);
my_echo(clientfd);
exit(0);
}
close(clientfd);
}
return 0;
}

回显客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <asm-generic/errno-base.h>

static const int MAXLINE = 4096;

int e_socket(int domain, int type, int protocol);

void my_cli(FILE* fd, int sockfd);

void my_cli(FILE* fd, int sockfd) {
char sendline[MAXLINE] = {0};
char recvline[MAXLINE]= {0};
while (fgets(sendline, MAXLINE, fd) != NULL) {
write(sockfd, sendline, strlen(sendline));

read(sockfd, recvline, MAXLINE);
fputs(recvline, stdout);
memset(recvline, 0, MAXLINE);
}
}

int e_socket(int domain, int type, int protocol){
int sockfd = socket(domain, type, protocol);
if (sockfd < 0)
{
exit(0);
}
return sockfd;
}

int main(int argc, const char *argv[])
{
int sockfd = e_socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(45678);
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
connect(sockfd, (sockaddr*)&servaddr, sizeof(servaddr));
my_cli(stdin, sockfd);
return 0;
}

运行效果