C语言网络编程(二)建立套接字通讯UDP

所谓socket套接字,指的是在网络通信以前建立的通信接口。进行网络连接以前,需要向系统注册申请一个新的socket。然后使用这个socket进行网络连接。
提示:套接字=传输层协议+端口号+IP地址。
在进行网络连接以前,需要用socket函数向系申请一个通信端口。这个函数的使用方法如下所示。
int socket(int domain, int type, int protocol);
在参数表表中,domain 指定使用何种的地址类型。type参数的作用是设置通信的协议类型。参数protocol用来指定socket所使用的传输协议编号。这一参数通常不具体设置,一般设置为0即可。
在使用这个函数建立套接字以前,需要在程序的最前面包含下面的头文件。
#include
#include  

如果建立套接字成功,则返回这个套接字的编号。如果不成功,则返回-1。这个函数可能发生的错误与含义如下所示。
EPROTONOSUPPORT:参数domain指定的类型不支持参数type或protocol指定的协议
ENFILE:核心内存不足,无法建立新的socket结构
EMFILE:进程文件表溢出,无法再建立新的套接字。
EACCESS :权限不足,无法建立type或protocol指定的协议。
ENOBUFS、ENOMEM:内存不足。
EINVAL:参数不合法。


注意:只创建套接字无法进行正常的网络体系,要想和其它计算机通信还必须借助于其它函数将建立的套接字与IP地址和端口号联系起来。

DEMO:

static int udp_example()
{
	int sockfd;
	sockfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd<0)
	{
		return -1;
	}
	return 0;
}

取得socket状态:函数getsockopt可以取得一个socket的参数。这个函数的使用方法如下所示。
int getsockopt(int s,int level,int optname,void* optval,socklen_t* optlen);
在参数列表中,s是已经建立socket的编号。level代表需要设置的网络层,一般设成SOL_SOCKET来表示socket层。参数optname代码的是需要获取的选项,可以设置成下面这些值。
SO_DEBUG:打开或关闭排错模式。
SO_REUSEADDR:允许在bind函数中本地地址可重复使用。
SO_TYPE:返回socket形态。
SO_ERROR:返回socket已发生的错误原因。
SO_DONTROUTE:送出的数据包不要利用路由设备来传输。
SO_BROADCAST:使用广播方式传送。
SO_SNDBUF:设置送出的暂存区大小。
SO_RCVBUF:设置接收的暂存区大小。
SO_KEEPALIVE:定期确定连线是否已终止。
SO_OOBINLINE:当接收到OOB 数据时会马上送至标准输入设备。
SO_LINGER:确保数据可以安全可靠传送出去。
参数optval是取得的某个参数的返回值指针,程序的返回值会保存在这个指针指向的变量中。optlen是optval的内存长度。函数如果执行成功则返回0,反之返回-1。这个函数可能发生下面这些错误。
EBADF:参数s不是合法的socket代码。
ENOTSOCK:参数s为一打开文件的编号,而不是一个socket。
ENOPROTOOPT:参数optname指定的选项不正确。
EFAULT:参数optval指针指向的内存空间无法读取。

设置socket状态:函数setsockopt可以设置一个socket的状态,这个函数的使用方法如下所示。
int setsockopt(int s,int level,int optname,const void * optval,socklen_toptlen);
在参数列表中,s是已经打开的socket。参数level代表欲设置的网络层,一般设成SOL_SOCKET以存取socket层。参数optname、optval、toptlen的含义与16.7.3小节中的这些参数作用相同。
如果函数设置socket成功则返回0,若有错误则返回-1。这个函数可能发生下面列出的错误。可以用errno捕获已经发生的错误。
EBADF:参数s不是合法的socket代码。
ENOTSOCK:参数s为一打开文件的编号,而不是一个socket。
ENOPROTOOPT:参数optname指定的选项不正确。
EFAULT:参数optval指针指向的内存空间无法读取。
使用这个函数以前,需要在程序的最前面包含下面的头文件。
#include
#include

具体使用方法可以参考这个博客http://blog.sina.com.cn/s/blog_a459dcf5010155nb.html

DEMO:设置和获取超时时间

static int udp_example()
{
	int sockfd,val,len,rs=-1;
	int time_out=5000;
	sockfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd<0)
	{
		return -1;
	}
	//获取&设置超时时间
	getsockopt(sockfd,SOL_SOCKET,SO_SNDTIMEO,&val,&len);
	if(len==0)
	{
		printf("time out:%d\n",val);
	}
	rs=setsockopt(sockfd,SOL_SOCKET,SO_SNDTIMEO,&time_out,sizeof(int));
	if(rs==0)
	{
		printf("set time out!\n");
	}
	getsockopt(sockfd,SOL_SOCKET,SO_SNDTIMEO,&val,&len);
	if(len==0)
	{
		printf("reset time out:%d\n",val);
	}
	//发送UDP请求
	return 0;
}
所谓无连接的套接字通信,指的是使用UDP协议进行信息传输。使用这种协议进行通信时,两个计算机之前没有建立连接的过程。需要处理的内容只是把信息发送到另一个计算机。这个通信的方式比较简单。本节将讲述这种无连接的UDP通信。
提示:由于无连接的套接字通信基于UDP协议,所以无法保证数据传输的可靠性。
工作流程:无连接套接字的通信不需要建立起客户机与服务器之间的连接,因些在程序中没有建立连接的过程。进行通信之前,需要建立网络套接字。服务器需要绑定一个端口,在这个端口上监听接收到的信息。客户机需要设置远程IP和端口,需要传递的信息需要发送到这个IP和端口上。

C语言网络编程(二)建立套接字通讯UDP_第1张图片

信息发送函数sendto:函数sendto可以通过一个已经建立的套接字,将一段信息发送到另一个程序的套接字中。这个函数的使用方法如下所示。
int sendto ( int s ,  void * msg, int len, unsigned int flags,struct sockaddr * to , int tolen ) ;
在参数列表中,s是已经建立好的socket。在使用UDP协议时,不需要进行计算机连接的操作。msg是需要发送的字符串。len是发送字符串的长度。参数flags一般设0,其他可能的数值如下所示。
MSG_OOB:传送的数据以out-of-band 送出。
MSG_DONTROUTE:取消路由表查询。
MSG_DONTWAIT:设置为不可阻断传输。
MSG_NOSIGNAL:此传输不愿被SIGPIPE 信号中断。
参数sockaddr是一个表示套接字的结构体。这个结构体的定义如下所示。
这个结构体的定义方法如下所示。
struct socketaddr_in
{
  unsigned short int sin_family;
  uint16_t sin_port;
  struct in_addr sin_addr;
  unsigned char sin_zero[8];
};
这个结构体的成员与作用如下所示。
sin_family:与sockaddr结构体中的sa_family相同。
sin_port:套接字使用的端口号。
sin_addr:需要访问的IP地址。
sin_zero:未使用的字段,填充为0。
在这一结构体中,in_addr也是一个结构体,定义方法如下所示。作用是保存一个IP地址。
struct in_addr
{
  uint32_t s_addr;
};
在程序中,需要设置这个结构体每一个成员的值。参数tolen是sockaddr结构体的长度,这一长度可用sizeof函数来取得。这个结构体会把socket用指定的socket传送给对方主机。结果传送成功,则返回传送字符的个数。传送失败则返回-1。传送失败时,错误原因保存在errno中,可能发生的错误如下所示。
EBADF:参数s不是一个正常的socket。
EFAULT:参数中的指针指向无法读取的内存空间。
WNOTSOCK:s为一文件描述词,而不是一个socket。
EINTR:被其他信号所中断。
EAGAIN:此动作会令进程阻断。
ENOBUFS:系统的缓冲内存不足。
EINVAL:传给系统调用的参数不正确。

信息接收函数recvfrom:函数recvfrom可以从一个socket中接收其他主机发送到来的信息。这个函数的使用方法如下所示。
int recvfrom(int s,void *buf,int len,unsigned int flags ,sockaddr *from ,int *fromlen); 
在参数列表中,s是一个已经建立一的网络套接字。buf是接收到的信息保存到的内存地址。len是可以保存信息的buf内存长度。flags一般设0其他可能的设置如下所示。
MSG_OOB:接收以out-of-band 送出的数据。
MSG_PEEK:返回来的数据不从系统内删除,如果再调用recv()会返回相同的数据内容。
MSG_WAITALL:强迫接收到len大小的数据后才能返回,除非有错误或信号产生。
MSG_NOSIGNAL:此操作不愿被SIGPIPE信号中断。
参数sockaddr是IP地址与端口等信息。fromlen是sockaddr的长度,这个值可以用sizeof函数来取得。
这个函数调用成功,则返回接收到的字符数。失败则返回-1,错误原因存于errno中。可能发生的错误如下所示。
EBADF:参数s不是一个正确的socket。
EFAULT:参数中的指针可能指向了无法读取的内存空间。
ENOTSOCK:参数s是文件描述词,而不是一个socket。
EINTR:被其他信号所中断。
EAGAIN:此动作会令进程阻断。
ENOBUFS:系统的缓冲内存不足。
ENOMEM:核心内存不足。
EINVAL:传给系统调用的参数不正确。

DEMO:

static int udp_example()
{
	int sockfd,val,len,rs=-1,fromlen;
	int time_out=5000;
	char buff[5000];

	sockfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd<0)
	{
		return -1;
	}
	//发送UDP请求
	struct sockaddr_in addr_in;
	addr_in.sin_family=AF_INET;
	addr_in.sin_port=htons(80);//网络使用的整数与程序使用的不同,需要转换
	addr_in.sin_addr.s_addr=inet_addr("1.1.1.1");
	bzero(&(addr_in.sin_zero),8);
	char *msg="GET /play.php HTTP/1.1\
Host: xxx.xxx.com\
Connection: keep-alive\
Cache-Control: max-age=0\
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\
Upgrade-Insecure-Requests: 1\
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36\
Accept-Encoding: gzip, deflate, sdch\
Accept-Language: zh-CN,zh;q=0.8\
Cookie: ";
	rs=sendto(sockfd,msg,strlen(msg),0,(struct sockaddr *)&addr_in,sizeof(struct sockaddr));
	printf("sendto %d bytes\n",rs);
	rs=recvfrom(sockfd,buff,sizeof(buff),0,(struct sockaddr *)&addr_in,&fromlen);
	printf("recvfrom %d bytes\n",rs);
	return rs;
}


你可能感兴趣的:(C/C++)