网络模型:
Socket编程模型:
Socket的实质就是一个接口, 利用该接口,用户在使用不同的网络协议时,操作函数得以统一。而针对不同协议的差异性操作,则交给了socket去自行解决。
创建Socket套接字
函数原形:
int socket(int domain, int type, int protocol);
函数功能: 创建socket套接字
所属头文件:
#include <sys/types.h>
#include <sys/socket.h>
返回值:
若成功,返回套接字的标识符;若失败,返回-1.
参数说明:
domain:指明了网络域,AF_INET,用于ipv4;AF_INET6,用于ipv6。
type:协议类型,SOCK_STREAM
上面两个参数填写如下:
如:UDP协议就是AF_INET 和 SOCK_DGRAM
如:TCP协议就是AF_INET 和 SOCK_STREAM
protocol: UDP协议和TCP协议这里可以为0.
绑定地址
函数原形:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
函数功能: 为套接字绑定相应的IP地址
所属头文件:
#include <sys/types.h>
#include <sys/socket.h>
返回值:
若成功,返回0;若失败,返回-1.
参数说明:
sockfd:指定的socket套接字的ID
*addr:要绑定的地址,这是bind函数用到的地址类型,通用类型,第一个成员,协议族的类型,第二个成员,具体的协议值
addrlen:地址的长度,addr指向内容的长度
注意:
上述地址结构体由于不方便赋值,因此在编程中不采用,使用下面的结构体:
struct sockaddr_in
{
short int sin_family;
unsigned short int sin_port;//2字节
struct in_addr sin_addr;//4字节
unsigned char sin_zero[8];//8字节
};
struct in_addr
{
unsigned long s_addr;//此处为整形的IP地址
};
两种结构中地址数据都为 14Bytes,该结构体专用于IPV4,且需要加上头文件:
#include <netinet/in.h>
设置监听端口
函数原形:
int listen(int sockfd, int backlog);
函数功能: 监听网络端口
所属头文件:
#include <sys/types.h>
#include <sys/socket.h>
返回值:
若成功,返回0;若失败,返回-1.
参数说明:
sockfd:指定监听其他端口的socket标识符ID(它去监听别人)
backlog:客户机的数目,允许多少个连接
等待连接函数
函数原形:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
函数功能:
等待客户机连接,若无连接,服务器阻塞,有链接,继续运行
所属头文件:
#include <sys/types.h>
#include <sys/socket.h>
返回值:
若成功,返回连接到的套接字的标识符;若失败,返回-1.
参数说明:
sockfd:正在监听的socket标识符,这是原始套接字ID,函数返回的是一个新的套接字ID,新ID连接到客户机,接收客户机传来的数据用新ID,原始ID可以接收其他连接请求。
*addr:客户机的地址
*addrlen:客户机的地址的长度,特别注意这里地址长度为指针
发送数据
函数原形:
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
函数功能: 从套接字上发送数据
所属头文件:
#include <sys/types.h>
#include <sys/socket.h>
返回值:
若成功,返回发送的字节数;若失败,返回-1.
参数说明:
sockfd:发送数据的套接字ID,就是数据来源方的socket套接字的ID
*buf:发送数据存在的位置
len:发送数据的长度
flags:标志,可以直接设为0
接收数据
函数原形:
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
函数功能:
从一个socket套接字接收数据
所属头文件:
#include <sys/types.h>
#include <sys/socket.h>
返回值:
若成功,返回接收到的字节数目;若失败,返回-1.
参数说明:
sockfd:连接成功后accept函数产生的新的套接字ID,数据来源方的ID。
*buf:接收数据存放的位置
len:接收数据的长度
flags:标志
关闭连接
close 与关闭文件函数相同用法
连接服务器
函数原形:
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
函数功能: 工作在客户机上,连接服务器
所属头文件:
#include <sys/types.h>
#include <sys/socket.h>
返回值:
若成功,返回0;若失败,返回-1.
参数说明:
sockfd:客户机的套接字
*addr:服务器的地址
addrlen:服务器的地址的长度
UDP发送数据
函数原形:
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
函数功能:
从套接字上发送数据,用于UDP协议中
所属头文件:
#include <sys/types.h>
#include <sys/socket.h>
返回值:
若成功,返回发送的字节数;若失败,返回-1.
参数说明:
sockfd:发送数据的套接字ID描述符
*buf:发送数据存在的位置
len:发送数据的长度
flags:标志,可以直接设为0
*dest_addr:目的IP地址
addrlen:目的地址长度
UDP接收数据
函数原形:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
函数功能:
从一个socket套接字接收数据,用于UDP协议
所属头文件:
#include <sys/types.h>
#include <sys/socket.h>
返回值:
若成功,返回接收到的字节数目;若失败,返回-1.
参数说明:
sockfd:连接成功后accept函数产生的新的套接字ID
*buf:接收数据存放的位置
len:希望接收数据的长度
flags:标志
*src_addr:保存数据的来源方的IP地址
*addrlen:保存来源地址的长度,注意这里是指针
TCP协议编程模型:
通信之前首先要连接,没连接就等待。
UDP协议编程模型:
若服务器没有接收到数据,就一直等待。
IP地址格式的转换:
• in_addr_t inet_addr(const char *cp)
功能:将字符串形式的IP地址转化为整数型的IP地址,整形的地址已经是网络字节序。
范例: in_addr.saddr = inet_addr(“192.168.1.1”);
• char *inet_ntoa (struct in_addr)
功能:将整数形式的IP地址转化为字符串形式的IP地址
网络字节序的转换:
网络通信中数据的发送和接收统一规定为大端模式,即:高位数据存放在低位地址处。因此只要数据大于等于2字节,都要进行如下转换:
- 发端: 从本机字节序转换为网络字节序
- 收端: 从网络字节序转换为本机字节序
uint32_t htonl(uint32_t hostlong);
将32位的数据从主机字节序转换为网络字节序
in_addr.saddr = htonl(INADDR_ANY);
//绑定所有的IP地址
uint16_t htons(uint16_t hostshort);
将16位的数据从主机字节序转换为网络字节序
uint32_t ntohl(uint32_t netlong);
将32位的数据从网络字节序转换为主机字节序
uint16_t ntohs(uint16_t netshort);
将16位的数据从网络字节序转换为主机字节序
例程:
1、TCP服务器,采用多进程实现并发。
/* 实现并发处理服务器,使用多进程 */
/* TCP协议中服务器的程序 */
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>
#define portnum 3333
void main()
{
int pid;//进程ID
int socket_server1;//服务器套接字标识符
int socket_server2;//服务器连接之后的套接字标识符
struct sockaddr_in server_addr;//服务器地址,使用的是IPV4地址结构
struct sockaddr_in client_addr;//客户机地址,使用的是IPV4地址结构
int size_addr;
char receive_buffer[128];//接收到的数据存放的位置
int receive_nbytes;//接收到的字节数目
//创建socket 套接字
socket_server1 = socket(AF_INET, SOCK_STREAM, 0);
if (socket_server1 == -1)//失败就退出
{
printf("creat socket of server fail.\n");
exit(1);
}
//设置要绑定的服务器地址
bzero(&server_addr, sizeof(struct sockaddr_in));//清零
server_addr.sin_family = AF_INET;//协议族
server_addr.sin_port = htons(portnum);//端口号,且 转换为网络字节序
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//绑定所有的IP地址,转换为网络字节序
//绑定地址
bind(socket_server1, (struct sockaddr *) (&server_addr),
sizeof(struct sockaddr));
//监听端口
listen(socket_server1, 5);
size_addr = sizeof(struct sockaddr);
while(1)
{
//等待连接
socket_server2 = accept(socket_server1,
(struct sockaddr *) (&client_addr),
(socklen_t *) (&size_addr));
//特别注意这里地址长度为指针
printf("server get connection from %s\n",
inet_ntoa(client_addr.sin_addr));
//一旦实现了连接,就创建一个子进程
pid = fork();
if (pid == 0)
{
//接收客户机数据
receive_nbytes = recv(socket_server2, receive_buffer, 128, 0);
receive_buffer[receive_nbytes] = '\0';//添加结束符
printf("server received %s\n", receive_buffer);
close(pid);
//结束新建立的连接
close(socket_server2);
exit(0);
}
}
//结束原始的连接
close(socket_server1);
}
2、TCP客户端
/* TCP协议中客户机的程序 */
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>
#define portnum 3333
void main()
{
int socket_client;//服务器套接字标识符
struct sockaddr_in server_addr;//服务器地址,使用的是IPV4地址结构
struct sockaddr_in client_addr;//客户机地址,使用的是IPV4地址结构
int connect_tmp;//判断连接是否成功
char buffer[128];//发送的数据存放的位置
int nbytes;//发送的字节数目
//创建socket 套接字
socket_client = socket(AF_INET, SOCK_STREAM, 0);
if (socket_client == -1)//失败就退出
{
printf("creat socket of client fail.\n");
exit(1);
}
//设置要连接的服务器地址
bzero(&server_addr, sizeof(struct sockaddr_in));//清零
server_addr.sin_family = AF_INET;//协议族
server_addr.sin_port = htons(portnum);//端口号,且 转换为网络字节序
server_addr.sin_addr.s_addr = inet_addr("192.168.234.129");
//指定IP地址,转换为整数类型的地址,已经是网络字节序
//连接服务器
connect(socket_client, (struct sockaddr *)(&server_addr),
sizeof(struct sockaddr));
if (connect_tmp == -1)
{
printf("connect fail.\n");
exit(1);
}
//发送数据
printf("Please input char.\n");
fgets(buffer, 128, stdin);
send(socket_client, buffer, strlen(buffer), 0);
//结束连接
close(socket_client);
}
3、UDP服务器
/* UDP协议中服务器的程序 */
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>
#define portnum 3333 //定义端口号
void main()
{
int socket_server;//服务器套接字标识符
struct sockaddr_in server_addr;//服务器地址,使用的是IPV4地址结构
struct sockaddr_in client_addr;//客户机地址,使用的是IPV4地址结构
int size_addr;//记录地址的长度
char receive_buffer[128];//接收到的数据存放的位置
int receive_nbytes;//接收到的字节数目
//创建socket 套接字
socket_server = socket(AF_INET, SOCK_DGRAM, 0);
if (socket_server == -1)//失败就退出
{
printf("creat socket of server fail.\n");
exit(1);
}
//设置要绑定的服务器地址
bzero(&server_addr, sizeof(struct sockaddr_in));//清零
server_addr.sin_family = AF_INET;//协议族
server_addr.sin_port = htons(portnum);//端口号,且 转换为网络字节序
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//绑定所有的IP地址,转换为网络字节序
//绑定地址
bind(socket_server, (struct sockaddr *) (&server_addr),
sizeof(struct sockaddr));
size_addr = sizeof(struct sockaddr *);//地址长度
while(1)
{
//接收客户机数据
bzero(receive_buffer, sizeof(receive_buffer));
receive_nbytes = recvfrom(socket_server, receive_buffer, 128, 0,
(struct sockaddr *)(&client_addr), (socklen_t *)(&size_addr));
receive_buffer[receive_nbytes] = '\0';//添加结束符
printf("server received %s\n", receive_buffer);
}
//结束连接
close(socket_server);
}
4、UDP客户端
/* UDP协议中客户机的程序 */
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>
#define portnum 3333 //定义端口号
void main(int argc, char **argv)
{
int socket_client;//服务器套接字标识符
struct sockaddr_in server_addr;//服务器地址,使用的是IPV4地址结构
struct sockaddr_in client_addr;//客户机地址,使用的是IPV4地址结构
char buffer[128];//发送的数据存放的位置
int nbytes;//发送的字节数目
//创建socket 套接字
socket_client = socket(AF_INET, SOCK_DGRAM, 0);
if (socket_client == -1)//失败就退出
{
printf("creat socket of client fail.\n");
exit(1);
}
//设置要发送数据的的目的地址
bzero(&server_addr, sizeof(struct sockaddr_in));//清零
server_addr.sin_family = AF_INET;//协议族
server_addr.sin_port = htons(portnum);//端口号,且 转换为网络字节序
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
//输入IP地址
//指定IP地址,转换为整数类型的地址,已经是网络字节序
while(1)
{
bzero(buffer, 128);
//发送数据
printf("Please input char.\n");
fgets(buffer, 128, stdin);
sendto(socket_client, buffer, strlen(buffer), 0,
(struct sockaddr *)(&server_addr), sizeof(server_addr));
}
//结束连接
close(socket_client);
}