套接字的特性由3个属性确定,它们分别是:域、端口号、协议类型。
(1)套接字的域
它指定套接字通信中使用的网络介质,最常见的套接字域有两种:
一是AF_INET,它指的是Internet网络。当客户使用套接字进行跨网络的连接时,它就需要用到服务器计算机的IP地址和端口来指定一台联网机器上的某个特定服务,所以在使用socket作为通信的终点,服务器应用程序必须在开始通信之前绑定一个端口,服务器在指定的端口等待客户的连接。
另一个域AF_UNIX,表示UNIX文件系统,它就是文件输入/输出,而它的地址就是文件名。
(2)套接字的端口号
每一个基于TCP/IP网络通讯的程序(进程)都被赋予了唯一的端口和端口号,端口是一个信息缓冲区,用于保留Socket中的输入/输出信息,端口号是一个16位无符号整数,范围是0-65535,以区别主机上的每一个程序(端口号就像房屋中的房间号),低于256的端口号保留给标准应用程序,比如pop3的端口号就是110,每一个套接字都组合进了IP地址、端口,这样形成的整体就可以区别每一个套接字。
(3)套接字协议类型
因特网提供三种通信机制,
一是流套接字SOCK_STREAM:流套接字在域中通过TCP/IP连接实现,同时也是AF_UNIX中常用的套接字类型。流套接字提供的是一个有序、可靠、双向字节流的连接,因此发送的数据可以确保不会丢失、重复或乱序到达,而且它还有一定的出错后重新发送的机制。
二是数据报套接字SOCK_DGRAM:它不需要建立连接和维持一个连接,它们在域中通常是通过UDP/IP协议实现的。它对可以发送的数据的长度有限制,数据报作为一个单独的网络消息被传输,它可能会丢失、复制或错乱到达,UDP不是一个可靠的协议,但是它的速度比较高,因为它并一需要总是要建立和维持一个连接。
三是原始套接字:原始套接字允许对较低层次的协议直接访问,比如IP、 ICMP协议,它常用于检验新的协议实现,或者访问现有服务中配置的新设备,因为RAW SOCKET可以自如地控制Windows下的多种协议,能够对网络底层的传输机制进行控制,所以可以应用原始套接字来操纵网络层和传输层应用。比如,我们可以通过RAW SOCKET来接收发向本机的ICMP、IGMP协议包,或者接收TCP/IP栈不能够处理的IP包,也可以用来发送一些自定包头或自定协议的IP包。网络监听技术很大程度上依赖于SOCKET_RAW。
RTP音频流传输的实现:
发送端:
#include
#include
#include
#include
#include
#include
#include
#define TS_PACKET_SIZE 188
#define MTU 1500
struct rtp_header{
unsigned char cc:4;
unsigned char x:1;
unsigned char p:1;
unsigned char v:2;
unsigned char pt:7;
unsigned char m:1;
unsigned short sequence_number;
unsigned int timestamp;
unsigned int ssrc;
};
void init_rtp_header(struct rtp_header *h){
h->v = 2;
h->p = 0;
h->x = 0;
h->cc = 0;
h->m = 0;
h->pt = 33;
h->sequence_number =0;
h->timestamp = 123;
h->ssrc =0;
}
void sequence_number_increase(struct rtp_header *header){
unsigned short sequence = ntohs(header->sequence_number);
sequence++;
header->sequence_number = htons(sequence);
}
int main(int argc,char *argv[]){
char buf[MTU];
unsigned int count = 0;
if(argc < 2){
printf(" need wav path \n");
return -1;
}
// Init RTP Header
init_rtp_header((struct rtp_header*)buf);
count = sizeof(struct rtp_header);
// Init socket
int sock = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in dest_addr;
dest_addr.sin_family=AF_INET;
dest_addr.sin_port = htons(5003);
dest_addr.sin_addr.s_addr =inet_addr("10.46.169.189");
// dest_addr.sin_addr.s_addr =INADDR_ANY;
bzero(&(dest_addr.sin_zero),8);
// Open TS file
FILE *ts_file = fopen(argv[1], "r+");
char h[44];
fread(h,1,44,ts_file);
int n=0;
while(!feof(ts_file)){
int read_len = fread(buf+count, 1, TS_PACKET_SIZE, ts_file);
// if(*(buf+count) != 0x47){
// fprintf(stderr, "Bad sync header!\n");
// continue;
// }
count += read_len;
// printf("count = %d\n",count);
if (count + TS_PACKET_SIZE > MTU){// We should send
printf("haha_count = %d\n",count);
sequence_number_increase((struct rtp_header*)buf);
sendto(sock, buf, count, 0, (const struct sockaddr*)&dest_addr, sizeof(dest_addr));
count = sizeof(struct rtp_header);
usleep(10000);
n++;
}
}
printf("numofcount:%d\n",n*1316);
fclose(ts_file);
}
接收端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct rtp_header{
unsigned char cc:4;
unsigned char x:1;
unsigned char p:1;
unsigned char v:2;
unsigned char pt:7;
unsigned char m:1;
unsigned short sequence_number;
unsigned int timestamp;
unsigned int ssrc;
};
void main(int argc, char **argv)
{
int socka;
int nPortA = 8000;
fd_set rfd; // 读描述符集
struct timeval timeout; // 定时变量
struct sockaddr_in addr; // 告诉sock 应该在什么地方licence
char recv_buf[1500]; // 接收缓冲区
int nRecLen; // 客户端地址长度!!!!!!
struct sockaddr_in cli; // 客户端地址
int nRet; // select返回值
socka = socket(AF_INET, SOCK_DGRAM, 0); // 创建数据报socka
if (socka == -1)
{
printf("socket()/n");
return;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; // IP协议
addr.sin_port = htons(nPortA); // 端口
addr.sin_addr.s_addr = htonl(INADDR_ANY); // 在本机的所有ip上开始监听
if (bind(socka, (struct sockaddr*)&addr, sizeof(addr)) == -1)// bind socka
{
printf("bind()/n");
return;
}
// 设置超时时间为6s
timeout.tv_sec = 6;
timeout.tv_usec = 0;
memset(recv_buf, 0, sizeof(recv_buf)); // 清空接收缓冲区
while (1)
{
FD_ZERO(&rfd); // 在使用之前总是要清空
// 开始使用select
FD_SET(socka, &rfd); // 把socka放入要测试的描述符集中
nRet = select(socka+1, &rfd, NULL, NULL, &timeout);// 检测是否有套接口是否可读
if (nRet == -1)
{
printf("select()\n");
return;
}
else if (nRet == 0) // 超时
{
//printf("timeout\n");
continue;
}
else // 检测到有套接口可读
{
if (FD_ISSET(socka, &rfd)) // socka可读
{
nRecLen = sizeof(cli);
int nRecEcho = recvfrom(socka, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&cli, &nRecLen);
if (nRecEcho == -1)
{
printf("recvfrom()\n");
break;
}
struct rtp_header *p = (struct rtp_header *)recv_buf;
printf("v = %d\n",p->v);
printf("p = %d\n",p->p);
printf("x = %d\n",p->x);
printf("cc = %d\n",p->cc);
printf("sequence_number = %d\n",ntohs(p->sequence_number));
printf("ssrc = %d\n",ntohs(p->ssrc));
}
}
}
}
另附一基于socket的聊天程序实现:
socket实例C语言:一个简单的聊天程序https://www.cnblogs.com/liushao/p/6375377.html