我们之前提到过,“IP地址+端口号”也就是socket可以唯一标识网络上某一主机的某一进程。所以我们要实现网络通信,我们要利用到socket。这里先介绍一些与socket有关的函数接口与结构。
socket API即套接字接口是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4和IPv6及UNIX Domain Socket。然而,各种网络协议的地址格式并不相同,所以就有以下几种地址格式。
struct sockaddr_in {
sa_family_t sin_family; /* Address family:地址类型(套接字类型) */
__be16 sin_port; /* Port number:16位端口号 */
struct in_addr sin_addr; /* Internet address:封装32位IP地址的结构体 */
/* Pad to size of `struct sockaddr'. : 8字节填充字段(这里不讨论)*/
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
struct in_addr {
__be32 s_addr;//32位的IP地址
};
函数原型:
函数原型:
实现过程:
//UDP服务器
#include
#include
#include
#include //close
#include //网络字节序和主机字节序库转换
#include //atoi
#include
#include
#define SIZE 128
void service(int sock)
{//这里我们提供的服务是:将客服端的消息拿到并打印,再将消息发回去
char buf[SIZE];
for( ; ; )//服务器应该周期性的一直提供服务,可用守护进程实现,这里先用死循环实现
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);//socklen_t 可认为简单整形
//udp不用read、write读写,它有自己个性化的接口
ssize_t s = recvfrom(sock, buf, sizeof(buf)-1, 0 , (struct sockaddr*)&peer, &len);
if(s > 0)//读成功
{
buf[s] = 0;//保证以\0结尾,上面参数sizeof-1就是给\0留位置
if(strcmp("quit", buf) == 0)
{
printf("client quit\n");
break;
}
printf("[%s:%d]# %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), buf);//将客户端发来的消息打印出来
sendto(sock, buf, strlen(buf), 0, (struct sockaddr*)&peer, len);//将内容给客户端发回去
}
}
}
//./udp_server 192.168.3.55 8080
int main(int argc, char* argv[])
{
if(argc != 3)//命令行输入格式不正确
{
printf("Usage: %s [IP] [port]\n", argv[0]);
return 3;
}
//1.创建套接字(这里是数据报的套接字,因为UDP面向数据报)
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock < 0)//创建套接字失败
{
printf("socket error\n");
return 1;
}
struct sockaddr_in local;
local.sin_family = AF_INET;//地址类型
local.sin_port = htons(atoi(argv[2]));//端口号;命令行的字符串”808"转换为整数的8080;主机序列转换为网络序列,16位
local.sin_addr.s_addr = inet_addr(argv[1]);//IP地址;将点分十进制转换为四字节地址(因为该字段在结构体中就是四字节);
//2.绑定(将文件信息与网络信息关联起来),绑定之后就可提供服务了
if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
{
printf("bind error\n");
return 2;
}
//3.提供服务
service(sock);
close(sock);//Linux下一切皆文件,能打开就要关闭
return 0;
}
//UDP客户端
#include
#include
#include
#include //close
#include //网络字节序和主机字节序库转换
#include //atoi
#include
#include
//./udp_client 127.0.0.1 8080
int main(int argc, char* argv[])
{
if(argc != 3)
{
printf("Usage:%s [ip] [port]\n", argv[0]);
return 1;
}
//客户端不需要绑定,因为它的端口号不需要特定
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock < 0)
{
printf("sock error\n");
return 2;
}
char buf[128];
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[2]));
server.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t len = sizeof(server);
for( ; ; )
{
buf[0] = 0;//清零
//1.从标准输入读数据
ssize_t s = read(0, buf, sizeof(buf)-1);
if(s > 0)//读成功
{
buf[s-1] = 0;//会把回车读进来,这里不要回车
//2.将读到的数据发出去
sendto(sock, buf, strlen(buf), 0, (struct sockaddr*)&server, len);//将数据发到socket
if(strcmp("quit", buf) == 0)
{
printf("client quit\n");
break;
}
//3.将发出的数据再收回来
s = recvfrom(sock, buf, sizeof(buf)-1, 0, NULL, NULL);//这里知道是谁发的消息,所以参数设置为NULL即可
if(s > 0)
{
buf[s] = 0;
printf("server echo# %s\n", buf);
}
}
}
close(sock);
return 0;
}