套接字
所谓socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。应用程 序通常通过"套接字"向网络发出请求或者应答网络请求。
sockaddr结构
sockaddr_in是网络套接字地址结构,大小为16字节,定义在<netinet/in>头文件中,一般我们在程序中是使用该结构体,但是作为参数传递给套接字函数时需要强转为sockaddr类型,注意该结构体中port和addr成员是网络序的(大端结构)。
为了执行网络IO,一个进程必须做的第一件事就是调用socket函数,指定期望的通信协议类型(比如使用IPv4的TCP、使用IPv6的UDP、Unix域字节流协议)和套接字字类型(字节流、数据报或原始套接字)。
#include <sys/socket.h> int socket(int family, int type, int protocol); // 成功返回非负描述符,出错-1
family指定协议族;
type指定套接字类型;
protocol指定某个协议类型常值,或者设为0。
family的值有:
- AF_INET IPv4协议(常用)
- AF_INET6 Ipv6协议
- AF_LOCAL Unix协议域
- AF_ROUTE 路由套接字
- AF_KEY 秘钥套接字
type的值有:
- SOCK_STREAM 字节流套接字
- SOCK_DGRAM 数据报套接字
- SOCK_SEQPACKET 有序分组套接字
- SOCK_RAW 原始套接字
protocol的值有:
- IPPROTO_CP TCP传输协议
- IPPROTO_UDP UDP传输协议
- IPPROTO_SCTP SCTP传输协议
网络地址的转换
网络IP地址本是用32位表示的。为了记忆方便,可以用点分十进制数来表示IP地址。同时,网路传输与计算机内部的字符存储方式是不同的,需要用相关的函数将端口进行转换。
函数inet_addr可以将一个网络IP地址转换成为一个十进制的长整数。
long inet_addr(char *cp)
函数的参数cp是一个IP地址字符串。使用函数之前,需要在程序中加入包含下面的头文件。
#inlcude<sys/socket,h>
#icnlude<netiet/in.h>
#include<arpa/inet.h>
主机字符顺序与网络字符顺序
计算机的字符与网络字符顺序是不同的。
uint32_t (uint32_t. hostlibg)
监听与连接
监听指的是socket的短裤处于等待状态,如果有客户端有连接请求,这个端口会接收这个连接。
连接指的是客户端 向服务器端发送一个通信申请,服务器端会响应这个操作。
listen函数
int listen(int s,int backlog)
listen()声明sockfd处于监听状态, 并且最多允许有backlog个客户端处于连接等待状态, 如果接收到更多的连接请求就忽略, 这⾥里设置不会太大(一般是5).
listen()成功返回0,失败返回-1
accept函数
int accept(int s,struct sockaddr *addr,int *addrlen);
connect函数
#include <sys/socket.h> int connect(int sockfd, const struct sockaddr* servaddr, socklen_t addrlen); // 返回:成功为0,出错-1
bind函数
#include <sys/socket.h> int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen); // 返回:成功为0,出错-1
udp服务端
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/*
* UDP的网络传输服务器端程序---简单聊天服务端程序
* 1. 创建socket socket
* 2. 为套接字绑定地址信息 bind
* 3. 数据传输 recvfrom/sendto
* 4. 关闭socket close
*/
int main()
{
int sockfd = -1;
//1. 创建套接字
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sockfd < 0) {
perror("socket");
return -1;
}
//2. 为套接字绑定地址信息
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9000);
addr.sin_addr.s_addr = inet_addr("192.168.122.131");
socklen_t len = sizeof(struct sockaddr_in);
int ret = bind(sockfd, (struct sockaddr*)&addr, len);
if (ret != 0) {
perror("bind");
return -1;
}
//3. 数据传输
while(1) {
//1. 接收客户端数据
struct sockaddr_in cli_addr;
char buff[1024] = {0};
len = 0;
recvfrom(sockfd, buff, 1023, 0,
(struct sockaddr*)&cli_addr, &len);
printf("client[%s:%d] say: %s\n",
inet_ntoa(cli_addr.sin_addr),
ntohs(cli_addr.sin_port),
buff);
//2. 向客户端回复数据
char tmp[1024] = {0};
printf("you say:");
fflush(stdout);
scanf("%s", tmp);
len = sizeof(struct sockaddr_in);
sendto(sockfd, tmp, strlen(tmp), 0,
(struct sockaddr*)&cli_addr, len);
}
close(sockfd);
return 0;
}
udp客户端
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
/*
* UDP客户端程序----聊天数据的发送与接收
* 1. 创建socket
* 2. 收发数据
* 3. 关闭socket
*/
int main()
{
int sockfd = -1;
//1. 创建socket
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sockfd < 0) {
perror("socket");
return -1;
}
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(9000);//绑定端口
serv_addr.sin_addr.s_addr = inet_addr("192.168.122.131");
socklen_t len = sizeof(struct sockaddr_in);
while(1) {
char buff[1024] = {0};
printf("you say:");
scanf("%s", buff);
sendto(sockfd, buff, strlen(buff), 0,
(struct sockaddr*)&serv_addr, len);
//recvfrom
char tmp[1024] = {0};
recvfrom(sockfd, tmp, 1023, 0,
(struct sockaddr*)&serv_addr, &len);
printf("serv say: %s\n", tmp);
}
close(sockfd);
return 0;
}