Fork me on GitHub

webRTC使用记录1

本小节是 webrtc 前置内容,主要介绍 tcp 跟 udp 的 server 及 client 端实现代码。

tips:c 语言实现,demo 级别,仅供一对一通讯。

参考自慕课网 百万级高并发WebRTC流媒体服务器设计与开发

udp 是无连接的,绑定了端口后直接进行收发处理,不需要监听

TCP连接相关结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct sockaddr_in{
sa_family_t sin_family; //什么协议,如 ipv4 or ipv6
uint16_t sin_port; // port addr
struct in_addr sin_addr; // ip addr
char sin_zero[8] // 默认 0
} // 用于服务端设置ip 和 port
struct in_addr{
in_addr_t s_addr; // ip addr,一般整型存储
}

struct sockaddr{
sa_family_t sin_family; //什么协议,如 ipv4 or ipv6
char sin_zero[8] // 默认 0
}

Tcp_server 端的c实现demo

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
// tcp_server.c
// 1. 创建 socket(socket_fd; 2. 配置 sin 相关; 3. bind; 4. listen;
// 5. accept & 生成 accept_fd; 5.5 loop 接收请求;
// 6. 结束时关闭 accept_fd; 7. 最终结束时关闭 socket_fd
//
//应该传入local_addr、remote_addr

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 8444
#define MESSAGE_SIZE 1024

int main(){
int ret = -1;
int socket_fd = -1;
int accept_fd = -1;
int curpos = 0;
int backlog = 10;
int flag = 1;
char in_buf[MESSAGE_SIZE] = {0,};
struct sockaddr_in local_addr, remote_addr;

//create a tcp socket
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if ( socket_fd == -1 ){
perror("create socket error");
exit(1);
}

//set option of socket
ret = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
if ( ret == -1 ){
perror("setsockopt error");
}

//set local address
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(PORT);
local_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(local_addr.sin_zero), 8);

//bind socket
ret = bind(socket_fd, (struct sockaddr *)&local_addr, sizeof(struct sockaddr_in));
if(ret == -1 ) {
perror("bind error");
exit(1);
}
ret = listen(socket_fd, backlog);
if ( ret == -1 ){
perror("listen error");
exit(1);
}

//loop
for(;;){
int addr_len = sizeof( struct sockaddr_in );
//accept an new connection
accept_fd = accept( socket_fd, (struct sockaddr *)&remote_addr, &addr_len );
for(;;){
memset(in_buf, 0, MESSAGE_SIZE);
//receive network data and print it
ret = recv( accept_fd ,(void*)in_buf ,MESSAGE_SIZE ,0 );
if(ret == 0 ){
break;
}
printf( "receive message:%s\n", in_buf );
send(accept_fd, (void*)in_buf, MESSAGE_SIZE, 0);
}
printf("close client connection...\n");
close(accept_fd);
}
printf("quit server...\n");
close(socket_fd);
return 0;
}

Tcp_client 端的 c 实现demo

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
// tcp_client.c
// 1. 创建 socket; 2. connect;
// 3. loop send & receive; 4. quit & close
//
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <netdb.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SERVER_PORT 8111
#define MESSAGE_LENGTH 1024

int main(){
int ret = -1;
int socket_fd;
//server addr
struct sockaddr_in serverAddr;
char sendbuf[MESSAGE_LENGTH];
char recvbuf[MESSAGE_LENGTH];
int data_len;

if((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
perror("socket");
return 1;
}
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(SERVER_PORT);
//inet_addr()函数,将点分十进制IP转换成网络字节序IP
serverAddr.sin_addr.s_addr = inet_addr("39.105.185.198");
//serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

// connect
if(connect(socket_fd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0){
perror("connect");
return 1;
}
printf("success to connect server...\n");
// loop send and receive data
while(1){
memset(sendbuf, 0, MESSAGE_LENGTH);
printf("<<<<send message:");
gets(sendbuf);
//printf("\n");
ret = send(socket_fd, sendbuf, strlen(sendbuf), 0);
if(ret <= 0 ){
printf("the connection is disconnection!\n");
break;
}
if(strcmp(sendbuf, "quit") == 0){
// 收到 quit 关键词,退出
break;
}
printf(">>> echo message:");
recvbuf[0] = '\0';
data_len = recv(socket_fd, recvbuf, MESSAGE_LENGTH, 0);
recvbuf[data_len] = '\0';
printf("%s\n", recvbuf);
}
close(socket_fd);
return 0;
}

UDP_server 端的 c 实现demo

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
// upd_server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>

int main(int argc, char * *argv){
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9876);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
char buff_recv[512] = {0};
char buff_send[512] = "world";
struct sockaddr_in clientAddr;
int n;
int len = sizeof(clientAddr);
int sock;
printf("Welcome! This is a UDP server.\n");

if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
printf("socket error.\n");
exit(1);
}
if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0){
printf("bind error.\n");
exit(1);
}
while (1){
n = recvfrom(sock, buff_recv, 511, 0, (struct sockaddr *) &clientAddr, &len);
if (n > 0){
buff_recv[n] = 0;
printf("recv data from client:%s %u says: %s\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port), buff_recv);
n = sendto(sock, buff_send, n, 0, (struct sockaddr *) &clientAddr, sizeof(clientAddr));
if (n < 0){
printf("sendto error.\n");
break;
}else {
printf("recv error.\n");
break;
}
}
return 0;
}

Udp_client 端的 c 实现demo

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
// udp_client.c

#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char * *argv){
struct sockaddr_in addr;
int sock;
addr.sin_family = AF_INET;
addr.sin_port = htons(9876);
addr.sin_addr.s_addr = inet_addr("111.231.68.13");
char buff_send[512] = "Hello";
char buff_recv[512] = {0};
int len = sizeof(addr);
int n = 0;
printf("This is a UDP client\n");
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
printf("socket error.\n");
exit(1);
}
if (addr.sin_addr.s_addr == INADDR_NONE){
printf("Incorrect ip address!");
close(sock);
exit(1);
}
n = sendto(sock, buff_send, strlen(buff_send), 0, (struct sockaddr *) &addr, sizeof(addr));
if (n < 0){
printf("sendto error.\n");
close(sock);
}
n = recvfrom(sock, buff_recv, 512, 0, (struct sockaddr *) &addr, &len);
if (n > 0){
buff_recv[n] = 0;
printf("received from sever:");
puts(buff_recv);
}
else if (n == 0)
printf("server closed.\n");
else if (n == -1)
printf("recvfrom error.\n");
close(sock);
return 0;
}

现在问题来了:

使用 while(1)或者其他死循环接收信息的方式,在多 client 的场景下,仅第一个连接上的 client 可以跟 server 通讯,其他 client 虽然也能连接上,但无法传输数据。

解决办法通常有:

  1. fork

  2. select

  3. epoll

  4. IO事件库

    详见下一节内容。

-------------The End-------------