报式套接字通讯实例
使用套接字通讯流程
被动端(先运行)
1、取得SOCKET
2、给SOCKET取得地址
3、收/发消息
4、关闭SOCKET
主动端
1、取得SOCKET
2、给SOCKET取得地址(可省略)
3、发/收消息
4、关闭SOCKET
各部分代码实现
proto.h代码如下:
#ifndef PROTO_H__
#define PROTO_H__
//定义端口号
#define RCVPORT "1989"
#define NAMESIZE 11
//__attribute__((packed))表示结构体取消内存对齐
struct msg_st
{
uint8_t name[NAMESIZE];
uint32_t math;
uint32_t chinese;
}__attribute__((packed));
#endif
rcvder.c代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "proto.h"
#define IPSTRSIZE 40
int main()
{
int sd;
//定义我端的地址laddr
struct sockaddr_in laddr,raddr;
struct msg_st rbuf;
socklen_t raddr_len;
char ipstr[IPSTRSIZE];
//使用AF_INET协议族中的默认协议(0),实现SOCK_DGRAM
sd = socket(AF_INET,SOCK_DGRAM,0);
if(sd < 0)
{
perror("socket()");
exit(0);
}
laddr.sin_family = AF_INET;
//因为端口要在网络传输,所以使用htons转换,将本地(host)转为网络(net),宽度为short
laddr.sin_port = htons(atoi(RCVPORT));
/*inet_pton将IP地址的点分式转为整数
int inet_pton(int af,const char *src,void *dst)
参数:
af 协议族,要么是IPV4(AF_INET)要么是IPV6(AF_INET6)
src 要转换的IP地址
dst 转换后要放置的地址处
*/
//"0.0.0.0"表示匹配的任意地址
inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);
//我端的地址和地址长度
if(bind(sd,(void *)&laddr,sizeof(laddr)) < 0)
{
perror("bind()");
exit(1);
}
while(1)
{
/*
ssize_t recv(int sockfd,void *buf,size_t len,int flags)
这个适用于流式套接字,一对一通讯
ssize_t recvfrom(int sockfd,void *buf,size_t len,int flags,
struct sockaddr *src_addr,socklen_t *addrlen)
recvfrom是适用于报式套接字,非一对一的通讯,要记录传输数据地址从哪里来的
*/
/*
一定要初始化raddr_len的大小,否则回造成第一次打开地址不对,后面会对
*/
raddr_len = sizeof(raddr);
recvfrom(sd,&rbuf,sizeof(rbuf),0,(void *)&raddr,&raddr_len);
//使用inet_ntop();函数将ip大整数转为点分式
inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);
printf("----MESSAGE FROM IP: %s port: %d---\n",ipstr,ntohs(raddr.sin_port));
printf("NAME = %s\n",rbuf.name);
printf("MATH = %d\n",ntohl(rbuf.math));
printf("CHINESE = %d\n",ntohl(rbuf.chinese));
}
close(sd);
exit(0);
}
编译执行结果如下:
使用netstat -anu命令查看udp网络状态,如果查看tcp使用netstat -ant命令,结果如下1989端口已经打开。
snder.c代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include "proto.h"
int main(int argc,char *argv[])
{
int sd;
//将待发送的内容放到sbuf中
struct msg_st sbuf;
struct sockaddr_in raddr;
if(argc < 2)
{
fprintf(stderr,"Usage is short 2\n");
exit(1);
}
sd = socket(AF_INET,SOCK_DGRAM,0);
if(sd < 0)
{
perror("socket()");
exit(1);
}
//bind();
/*
ssize_t send(int sockfd,const void *buf,size_t len,int flags);
这个是用于流式套接字,因为式一对一通讯
ssize_t sendto(int sockfd,const void *buf,size_t len,int flags,
const struct sockaddr *dest_addr,socklen_t addrlen);
这个用于报式套接字,因为是一对多,所以要指定目的的地址和大小
参数:
sockfd socket函数的返回值
buf 要发送的数据
len 发送数据的长度,即下一个参数buf的大小
flags 有无特殊要求,如无设置0选择默认
dest_addr 目的地址
addrlen 的地址的大小
返回值
成功 送出去的字符个数
失败 -1
*/
//填充sbuf要发送的数据
strcpy(sbuf.name,"Alan");
sbuf.math = htonl(rand()%100);
sbuf.chinese = htonl(rand()%100);
//设置远端的地址,分别设置协议族、端口、IP地址
raddr.sin_family = AF_INET;
raddr.sin_port = htons(atoi(RCVPORT));
inet_pton(AF_INET,argv[1],&raddr.sin_addr);
if(sendto(sd,&sbuf,sizeof(sbuf),0,(void *)&raddr,sizeof(raddr)) < 0)
{
perror("sento()ssfd");
exit(1);
}
puts("OK!");
close(sd);
exit(0);
}
编译运行结果如下:
使snder与rcver建立通信,先执行./rcver,再执行./snder,执行结果如下:
编译运行结果如下: