赞
踩
socket类型
提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。
提供无连接服务。数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。
可以对较低层次协议如IP、ICMP直接访问。
服务器:
1.创建流式套接字(socket())------------------------> 有手机
2.指定本地的网络信息(struct sockaddr_in)----------> 有号码
3.绑定套接字(bind())------------------------------>绑定电话
4.监听套接字(listen())---------------------------->待机
5.链接客户端的请求(accept())---------------------->接电话
6.接收/发送数据(recv()/send())-------------------->通话
7.关闭套接字(close())----------------------------->挂机
客户端:
1.创建流式套接字(socket())----------------------->有手机
2.指定服务器的网络信息(struct sockaddr_in)------->有对方号码
3.请求链接服务器(connect())---------------------->打电话
4.发送/接收数据(send()/recv())------------------->通话
5.关闭套接字(close())--------------------------- >挂机
int socket(int domain, int type, int protocol);
//作用:创建一个socket通信描述符
domain:指定通信的域(通信协议)
AF_UNIX, AF_LOCAL 本地通信
AF_INET ipv4
AF_INET6 ipv6
type:指定socket的类型
SOCK_STREAM:流式套接字,接下来我们的通信使用TCP协议
SOCK_DGRAM:数据报套接字,接下来我们的通信使用UDP协议
protocol:填0
返回值:如果成功,返回创建的描述符,如果失败,返回-1
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
作用:请求连接服务器
参数:
sockfd:上面socket接口得到的描述符
addr:相当于服务器的地址(IP+port)
addrlen:地址的长度,因为前面的地址是可变的,所以要通过参数来协定地址的长度
返回值:
0 -1
//从bind接口的帮助文档中拿到 struct sockaddr { sa_family_t sa_family; char sa_data[14]; } 上述地址结构是一个通用结构,我们在用实际协议进行通信的时候,需要转换成相应协议的结构体。 用man 7 ip来查看ipv4对应的结构体 struct sockaddr_in { sa_family_t sin_family; /* 地址协议族,=socket接口第一个参数 */ in_port_t sin_port; /* 指定端口,端口要用网络字节序(大端) */ struct in_addr sin_addr; /* IP地址 */ }; /* Internet address. */ struct in_addr { uint32_t s_addr; /* address in network byte order */ };
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
作用:绑定服务器地址:IP和端口,相当于对外公开自己的IP和端口,客户端就可以连接了
addr:绑定的IP和端口结构体,注意绑定的是自己的IP和端口
IP:取真实的某个网卡的地址,也可以直接写0.0.0.0
addrlen:地址的大小
int listen(int sockfd, int backlog);
作用:进入监听状态,以便接收客户端的连接
sockfd:上面的服务器描述符
backlog:同时能处理的客户端的连接数量,写个5 10都可以
返回值:0 -1
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
接收客户端连接,注意,这是一个阻塞接口,如果没有新的连接过来,那么会等待
sockfd:上面的服务器描述符
addr:客户端的地址(来电显示)
addrlen:客户端地址的长度,是入参,传入然后可能会被接口修改
返回值:如果成功,会返回一个非负的整数,代表接收的新的连接的描述符
//recv和send是网络的专用接口,比起read和write只是多了一个flags参数,flag一般情况下会取0,代表阻塞接受和发送
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
返回值:
>0:接收的字节数
<0:失败
=0:代表对端退出了
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
int close(int fd);
关闭套接字连接
#include <stdio.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #include <netinet/ip.h> /* superset of previous */ #include <arpa/inet.h> int main(int argc, char const *argv[]) { //创建套接字 int fd = socket(AF_INET, SOCK_STREAM, 0); if(fd < 0) { perror("socket err"); return -1; } //连接到服务器 struct sockaddr_in server_addr; int len = sizeof(server_addr); //指定连接的服务器的地址(IP+port) memset(&server_addr, 0, len); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8888); server_addr.sin_addr.s_addr = inet_addr("192.168.0.109"); int ret = connect(fd, (struct sockaddr *)&server_addr, len); if(ret < 0) { perror("connect err"); return -1; } printf("conect success\n"); char buf[64] = {0}; while (1) { memset(buf, 0, 64); //从终端接收用户输入,发给服务器,等待服务器的数据,打印 gets(buf); if(strcmp(buf, "quit") == 0) { break; } send(fd, buf, 64, 0); memset(buf, 0, 64); ret = recv(fd, buf, 64, 0); if(ret > 0) { printf("recv data = %s\n", buf); } else if(ret == 0) { printf("peer exit\n"); break; } else { perror("recv err\n"); return -1; } } close(fd); return 0; }
#include <stdio.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #include <netinet/ip.h> /* superset of previous */ #include <arpa/inet.h> int main(int argc, char const *argv[]) { //创建服务器的套接字 int server_fd = socket(AF_INET, SOCK_STREAM, 0); if(server_fd < 0) { perror("socket err"); return -1; } //初始化服务器地址 struct sockaddr_in server_addr, client_addr; int len = sizeof(server_addr); memset(&server_addr, 0, len); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8888); #if 0 server_addr.sin_addr.s_addr = inet_addr("192.168.0.194"); #else server_addr.sin_addr.s_addr = INADDR_ANY; #endif //绑定套接字的地址 int ret = bind(server_fd, (struct sockaddr *)&server_addr, len); if(ret < 0) { perror("bind err"); return -1; } //启动监听 ret = listen(server_fd, 5); if(ret < 0) { perror("listen err"); return -1; } //接收连接 int clientfd = accept(server_fd, (struct sockaddr *)&client_addr, &len); if(clientfd < 0) { perror("accept err"); return -1; } //打印客户端的地址,注意端口要转成主机字节序 printf("recv a new connect, ip = %s, port=%d\n",\ inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); char buf[64] = {0}; while (1) { memset(buf, 0, 64); //一旦新的连接过来后,后续通信统统使用新的描述符 len = read(clientfd, buf, 64); if(len > 0) { printf("recv data = %s\n", buf); } else if(len == 0) { printf("client exit\n"); break; } else { perror("recv err\n"); return -1; } } close(clientfd); close(server_fd); return 0; }
同属于传输层协议
TCP:流式套接字,面向连接的,可靠的通信
UDP:数据报套接字,无连接的,不可靠的通信
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); 作用:接收UDP对端发来的消息 sockfd:描述符 buf:接收缓冲区 len:缓冲区的长度 flags:填0,阻塞接收 src_addr:收到消息后,对端的地址存到这里(IP+PORT) addrlen:src_addr缓冲区长度 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); 作用:发送消息给UDP对端 sockfd:描述符 buf:发送缓冲区 len:发送的长度 flags:填0,阻塞发送 dest_addr:发送的目的地址(IP+PORT) addrlen:dest_addr的长度
PS:UDP的端口和TCP的端口是独立的!!
#include <stdio.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #include <netinet/ip.h> /* superset of previous */ #include <arpa/inet.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define KB 1024 int main(int argc, char const *argv[]) { //创建服务器的套接字 int server_fd = socket(AF_INET, SOCK_DGRAM, 0); if(server_fd < 0) { perror("socket err"); return -1; } //初始化服务器地址 struct sockaddr_in server_addr, client_addr; int len = sizeof(server_addr); memset(&server_addr, 0, len); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8888); #if 0 server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); #else server_addr.sin_addr.s_addr = INADDR_ANY; #endif //绑定套接字的地址 int ret = bind(server_fd, (struct sockaddr *)&server_addr, len); if(ret < 0) { perror("bind err"); return -1; } //接收消息,收到客户端消息,然后把客户端的消息再返回给客户端 char buf[KB] = {0}; struct sockaddr_in cli_addr; socklen_t addrlen = sizeof(struct sockaddr_in); while (1) { memset(buf, 0, KB); //服务器一定是先接收的 len = recvfrom(server_fd, buf, KB, 0, (struct sockaddr *)&cli_addr, &addrlen); if(len < 0) { perror("recv err"); return -1; } printf("recv from %s--%d,data=%s\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), buf); //接收成功,而且对端地址存储到了cli_addr中 sendto(server_fd, buf, len, 0, (struct sockaddr *)&cli_addr, addrlen); } close(server_fd); return 0; }
#include <stdio.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #include <netinet/ip.h> /* superset of previous */ #include <arpa/inet.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define KB 1024 int main(int argc, char const *argv[]) { //创建套接字 int fd = socket(AF_INET, SOCK_DGRAM, 0); if(fd < 0) { perror("socket err"); return -1; } //连接到服务器 struct sockaddr_in server_addr; int len = sizeof(server_addr); //指定连接的服务器的地址(IP+port) memset(&server_addr, 0, len); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8888); server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); char buf[KB] = {0}; socklen_t addrlen = sizeof(struct sockaddr_in); while (1) { memset(buf, 0, KB); gets(buf); sendto(fd, buf, strlen(buf), 0, (struct sockaddr *)&server_addr, addrlen); memset(buf, 0, KB); len = recvfrom(fd, buf, KB, 0, NULL, NULL); printf("recv from server data = %s\n", buf); } close(fd); return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。