什么是协议
先想象一下这样一个情形:如果相隔很远的两个人要展开对话,必须先决定对话方式。如果一方使用电话,那么另一方也只能使用电话,而不是书信。可以说,这里电话就是两人对话的协议,协议是对话中使用的通信规则。拓展到计算机,协议就是为了完成数据交换而定好的约定。
创建套接字
1,先来看看创建套接字的函数:
int socket(int domain, int type, int protocol);
成功返回文件描述符(整型),失败返回-1。
domain:协议族,type:数据传输类型,protocol:具体协议
2,下面再来分别谈谈创建套接字函数各参数意义:
协议族:奶油意大利面和番茄意大利面均属于意大利面的一种,与之类似,套接字中的协议也有分类,每一类别就称作一个协议族。即上面奶油意大利面和番茄意大利面类似协议,意大利面类似协议族。我们常用的协议族是PF_INET(IPv4互联网协议族)。
套接字类型:是指套接字数据传输的方式,下面介绍2种具有代表性的数据传输方式:
一:面向连接的套接字(SOCK_STREAM),这个和两个工人用传送带传输与接收物品类似,它有以下特性:传输过程中数据不会丢失,按序传输数据,没有数据边界,连接必须一一对应。
注释:没有数据边界是指,数据的发送次数与接收次数没有直接关系,不一定要相等。其实现原理就是在发送与接收端各用了个缓冲区,发送与接收端都是只管从缓冲区里取和拿数据,不直接相关。而且就算某个缓冲区满了也不会丢失数据,因为面向连接的套接字会根据接收端的状态来传输数据,如果缓冲区满了,它会自动停止传输。
二:面向消息的套接字(SOCK_DGRAM),这个就类似快递送包裹,有以下特点:强调传输速度不必按序传输,数据可能丢失,每次传输数据大小有限制,有数据边界(传输次数与接收次数要一一对应),不存在连接的概念。
协议:socket()第三个参数就是具体协议的选择,如:
“IPv4协议族中面向连接的套接字”
int tcp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
因为IPv4协议族中面向连接的套接字的具体协议只有一种就是IPPROTO_TCP,所以第三个参数我们也可以简写成0。这种套接字我们称为TCP套接字。
“IPv4协议族中面向消息的套接字”
int udp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
同上,这里第三个参数也可以写成0。这种套接字我们称为UDP套接字。
代码示例
下面是一个tcp套接字的实例,从中就可以看出tcp套接字没有数据边界。
服务端
//
// main.cpp
// hello_server
//
// Created by app05 on 15-7-6.
// Copyright (c) 2015年 app05. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, const char * argv[]) {
int serv_sock;
int clnt_sock;
struct sockaddr_in serv_addr;
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size;
char message[] = "Hello World!";
if(argc != 2)
{
printf("Usage:%s <port>\n", argv[0]);
exit(1);
}
/*IPv4协议族中面向连接的套接字(tcp套接字),因为这种类型下的具体协议只有一种(IPPROTO_TCP),
所以第三个参数可以简写为0*/
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
if(serv_sock == -1)
error_handling("socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(argv[1]));
if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)
error_handling("bind() error");
if(listen(serv_sock, 5) == -1)
error_handling("listen() error");
clnt_addr_size = sizeof(clnt_addr);
//如果没有收到请求,则不返回,只到有连接请求为止
clnt_sock = accept(serv_sock, (struct sockaddr*) &clnt_addr, &clnt_addr_size);
if(clnt_sock == -1)
error_handling("accept() error");
//message数组内容一次性发送
write(clnt_sock, message, sizeof(message));
close(clnt_sock);
close(serv_sock);
return 0;
}
客服端
//
// main.cpp
// hello_client
//
// Created by app05 on 15-7-6.
// Copyright (c) 2015年 app05. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, const char * argv[]) {
int sock;
struct sockaddr_in serv_addr;
char message[30];
int str_len = 0;
int idx = 0, read_len = 0;
if(argc != 3)
{
printf("Usage: %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_STREAM, 0);
if(sock == -1)
error_handling("socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
serv_addr.sin_port = htons(atoi(argv[2]));
if(connect(sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)
error_handling("connect() error");
//每次只接收一个字节(服务端一次性发送,客服端多次接收,验证tcp套接字没有数据边界)
while (read_len = read(sock, &message[idx++], 1)) {
if(read_len == -1)
error_handling("read() error!");
str_len += read_len;
}
printf("Message from server: %s \n", message);
printf("Function read call count: %d \n", str_len);
close(sock);
return 0;
}
————————————————
版权声明:本文为CSDN博主「夜色魅影」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u010223072/article/details/46813651