linux服务器socket编程

一、socket介绍

如今社会是万物互联的时代,因此网络成为了最为关键的部分。人能通过人的大脑根据人的外貌特征来准确的区分。那么计算机是怎么识别计算机的呢,这时我们需要给它门一个身份来确定。网络层的“ip地址”能够识别网络中的唯一主机,“端口”能够确定主机中的唯一进程,这样确定了地址信息之后就能进行通信了。如今采用TCP/IP协议大多采用socket进行通信,而且大多部分多着C/S模型,所以学习socket对于嵌入式来说是必不可少的。

二、创建socket服务器的流程

在这里插入图片描述
服务器的过程与客户端相差不是很大,有一点不同的是,客户端创建socket时linux内核将自动分配IP地址和端口,服务器创建socket时需要自己设定IP地址和端口,这是时后将调用bind()函数来进设置,然后调用listen监听,当有客户端申请连接时则采用accept进行连接。

三、函数的介绍与运用。

对于大多数函数socket write read close这些已经在这篇博客socket客户端
讲到,现在主要讲讲bind listen accept这些函数。

1、bind

bind(系统调用)函数原型:
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
*将addr中的信息(地址,端口等)分配给sockfd这个套接字。bind函数有如下三个参数:

sockfd:socket所创建的socket描述字,bind将给它addr内的信息。
adddr:一个const struct sockaddr *的指针,指向要绑定sockfd的地址
addlen:addr的长度。

返回值:如果成功,则返回零。出现错误时,将返回-1,并正确设置errno。
示例代码:

memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port   = htons(port);
	servaddr.sin_addr.s_addr  = htonl(INADDR_ANY);

	rb=bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

首先将servaddr初始化,然后对servaddr进行赋值,servaddr.sin_family = AF_INET;表示地址类型为IPV4(IPV6的话用AF_INET6)。servaddr.sin_port = htons(port);设置绑定的主机端口,注意一定要进行字节转换。servaddr.sin_addr.s_addr = htonl(INADDR_ANY);INADDR_ANY代表着任意的IP地址都能够访问,一般服务器都是这个模型,如果要指定IP连接的话只要对servaddr.sin_addr赋值相应的IP地址即可。

2、listen

listen(系统调用)原型:
int listen(int sockfd, int backlog);
函数功能:listen()将sockfd所指的套接字标记为一个被动套接字,即一个将用于接受一个正在使用connect连接的客户端并转给accept相应。

参数:
sockfd:socket()系统调用创建的要监听的socket描述字
backlog:内核请求连接的socket队列的最大个数。 TCP建立连接是要进行三次握手,但是否完成三次握手后,服务器需要维护这种状态:

半连接状态为:服务器处于Listen状态时收到客户端SYN报文时放入半连接队列中,即SYN queue(服务器端口状态 为:SYN_RCVD)。
 全连接状态为:TCP的连接状态从服务器(SYN+ACK)响应客户端后,到客户端的ACK报文到达服务器之前,则一 直保留在半连接状态中;

返回值:如果成功,则返回零。出现错误时,将返回-1,并正确设置errno。
代码示例:

listen(sockfd, 20);

允许最多同时20个客户端进行排队。
3、accept

accept(系统调用)原型:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
listen激活后,内核会调用accept接受客户端的请求,这个函数默认是一个阻塞函数,一旦客户端调用connect()函数就会触发服务器的accept()返回,这时整个TCP链接就建立好 了。其返回值会返回一个新的socket描述字用来跟客户端相连,然后原来的sockfd继续监听着。

参数:
sockfd:服务器用socket所创建并bind 和listen的socket描述字。
addr:返回客户端的协议地址,有IP地址 端口等信息。
addrlen:返回客户端地址协议的长度。

返回值:成功时,这些系统调用返回一个非负整数,该整数是接受套接字的文件描述符。出错时,-1返回,并正确设置errno。
示例代码

clidfd=accept(sockfd, (struct sockaddr *)&clidaddr, &len);
		if(clidfd<0)
		{
			 printf("accept clinet failure:%s\n",strerror(errno));
			 continue;
		}
		
		printf("accept client ip[%s] port[%d]\n",inet_ntoa(clidaddr.sin_addr),ntohs(clidaddr.sin_port));

此时,accept成功后会将客户端的信息保存在addr中,如需获得客户端的IP地址 端口等信息,此时读出addr中的内容即可(注意将网络字节序转换为主机字节序)。

四、源代码
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <getopt.h>





#define MSEG_SER "hello i am server"


int main(int argc ,char **argv)
{
	int                  sockfd = -1;
	int                  on = 1;
	struct sockaddr_in   servaddr;
	struct sockaddr_in   clidaddr;
	int                  port = 0;
	int                  rb = -1;
	socklen_t            len;
	int                  clidfd = -1;
	char                 buf[1024];

	port = atoi(argv[1]);

	sockfd=socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0)
	{
		printf("creat sockfd failure:%s\n",strerror(errno));
		return -1;
	}
	printf("creat sockfd sucessful:%d\n",sockfd);

	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port   = htons(port);
	servaddr.sin_addr.s_addr  = htonl(INADDR_ANY);

	rb=bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
	if(rb < 0)
	{
		printf("socket bind port(%d) failer:%s\n",port,strerror(errno));
		return -2;
	}

	listen(sockfd, 20);
	printf("listen to port[%d] sucessful\n",port);
	memset(&clidaddr, 0, sizeof(clidaddr));

	while(1)
	{
		printf("waitiing client connect\n");

		clidfd=accept(sockfd, (struct sockaddr *)&clidaddr, &len);
		if(clidfd<0)
		{
			 printf("accept clinet failure:%s\n",strerror(errno));
			 continue;
		}
		
		printf("accept client ip[%s] port[%d]\n",inet_ntoa(clidaddr.sin_addr),ntohs(clidaddr.sin_port));

		if((rb = read(clidfd, buf, sizeof(buf)))<0)
		{
			printf("read data from clinet failure:%s\n",strerror(errno));
			close(clidfd);
			continue;
		}
		else if(rb == 0) 
		{ 
			printf("no message from clinet\n");
			close(clidfd);
			continue;
		}
		else if(rb > 0)
		{
			printf("get message %d byte from clinet:%s\n",rb,buf);
		}

		if((rb = write(clidfd, MSEG_SER, strlen(MSEG_SER))) < 0)
		{
			printf(" write date to clinet failure:%s\n",strerror(errno));
			close(clidfd);
			continue;
		}

		printf("write %d byte data to clinet :%s\n",rb,strerror(errno));
		close(clidfd);

	}

	close(sockfd);


}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值