您的位置:首页 > 理论基础 > 计算机网络

TCP/IP网络编程 学习笔记_2 --套接字类型与协议设置

2015-07-09 10:38 741 查看

什么是协议

先想象一下这样一个情形:如果相隔很远的两个人要展开对话,必须先决定对话方式。如果一方使用电话,那么另一方也只能使用电话,而不是书信。可以说,这里电话就是两人对话的协议,协议是对话中使用的通信规则。拓展到计算机,协议就是为了完成数据交换而定好的约定。

创建套接字

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;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: