#網絡程序設計#(五)其他套接字API接口示例

更多網絡程序設計的文章見:目錄

recv、send

ssize_t recv_peek(int sockfd, void *buf, size_t len)//與原型相比去掉了flags
{//可以從套接口接收數據,可以指定不將緩衝區數據移除
	while(1)
	{
		int ret = recv(sockfd, buf, len, MSG_PEEK);
		if(ret == -1 && errno == EINTR)
			continue;
		return ret;
	}
}

readline實現

//暗含讀取方法,標準是一個個讀取,但效率太低,因此用偷窺的方法
//recv_peek不可重入
ssize_t readline(int sockfd, void *buf, size_t maxline)
{
	int ret;
	int nread;
	char *bufp = buf;
	int nleft = maxline;
	while(1)
	{
		ret = recv_peek(sockfd, bufp, nleft);
		if(ret < 0)
			return ret;
		else if(ret == 0)
			return ret;//對方關閉了套接口

		nread = ret;
		int i;
		for(i = 0; i < nread; i++)//最簡單情況,有一條消息,我們讀走
		{
			if(bufp[i] == '\n')
			{
				ret = readn(sockfd, bufp, i + 1);
				if(ret != i+1)
					exit(EXIT_FAILURE);
				return ret;
			}
		}
		if(nread > nleft)
			exit(EXIT_FAILURE);

		nleft -= nread;
		ret = readn(sockfd, bufp, nread);
		if(ret != nread)
			exit(EXIT_FAILURE);

		bufp += nread;
	}
	return -1;
}

用readline實現回射客戶/服務器

  • echosrv.c
#include<unistd.h>
 
#include <sys/types.h>
#include <sys/socket.h>
 
 
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>//信號,進程間通信
 
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
 
#define ERR_EXIT(m)  \
	do\
	{\
		perror(m);\
		exit(EXIT_FAILURE);\
	}while (0)

 
ssize_t readn(int fd, void *buf, size_t count)
{
        size_t nleft = count;
        ssize_t nread;
        char *bufp = (char*)buf;

        while(nleft > 0)
        {
                if((nread = read(fd, bufp, nleft)) < 0)
                {
                        if(errno == EINTR)
                                continue;
                        return -1;
                }
                else if(nread == 0)
                        return count - nleft;

                bufp += nread;
                nleft -= nread;
        }

        return count;
}

ssize_t writen(int fd, const void *buf, size_t count)
{
        size_t nleft = count;
        ssize_t nwritten;
        char *bufp = (char*)buf;

        while(nleft > 0)
        {
                if((nwritten = write(fd, bufp, nleft)) < 0)
                {
                        if(errno == EINTR)
                                continue;
                        return -1;
                }
                else if(nwritten == 0)
                        continue;

                bufp += nwritten;
                nleft -= nwritten;
        }

        return count;
}

ssize_t recv_peek(int sockfd, void *buf, size_t len)//與原型相比去掉了flags
{//可以從套接口接收數據,可以指定不將緩衝區數據移除
	while(1)
	{
		int ret = recv(sockfd, buf, len, MSG_PEEK);
		if(ret == -1 && errno == EINTR)
			continue;
		return ret;
	}
	
}

//暗含讀取方法,標準是一個個讀取,但效率太低,因此用偷窺的方法
//recv_peek不可重入
ssize_t readline(int sockfd, void *buf, size_t maxline)
{
	int ret;
	int nread;
	char *bufp = buf;
	int nleft = maxline;
	while(1)
	{
		ret = recv_peek(sockfd, bufp, nleft);
		if(ret < 0)
			return ret;
		else if(ret == 0)
			return ret;//對方關閉了套接口

		nread = ret;
		int i;
		for(i = 0; i < nread; i++)//最簡單情況,有一條消息,我們讀走
		{
			if(bufp[i] == '\n')
			{
				ret = readn(sockfd, bufp, i + 1);
				if(ret != i+1)
					exit(EXIT_FAILURE);
				return ret;
			}
		}
		if(nread > nleft)
			exit(EXIT_FAILURE);

		nleft -= nread;
		ret = readn(sockfd, bufp, nread);
		if(ret != nread)
			exit(EXIT_FAILURE);

		bufp += nread;
	}
	return -1;

}

void do_service(int conn)
{
	char recvbuf[1024];	
	while(1)
	{
		memset(recvbuf, 0,sizeof(recvbuf));
		int ret = readline(conn, recvbuf, 1024);
		if(ret == -1)
			ERR_EXIT("readline");
		if(ret == 0)
		{
			printf("client close\n");
			break;
		}		

		
		fputs(recvbuf, stdout);
		writen(conn, recvbuf, strlen(recvbuf));
	}
} 


int main(void)
{
	int listenfd;//被動套接字
	if((listenfd = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)//創建套接字小於0表示失敗
/*	if((listenfd = socket(PF_INET,SOCK_STREAM,0))<0);*///讓內核自己選定協議
		ERR_EXIT("socket");
 
	struct sockaddr_in servaddr;
	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(5188);
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//定義IP地址INTADDR_ANY這個的意思是本機全部地址(推薦)
	//第二種方法
	//servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");//定義IP地址	
	//第三種方式
	//inet_aton("10.18.95.29",&servaddr.sin_addr);
 
	int on = 1;//開啓地址重複利用
	if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0)
		ERR_EXIT("setsockopt");
 
	if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
		ERR_EXIT("bind");
 
	if(listen(listenfd,SOMAXCONN)<0)//監聽後變爲被動套接字
		ERR_EXIT("listen");
	
	struct sockaddr_in peeraddr;
	socklen_t peerlen = sizeof(peeraddr);
	int conn;//主動套接字

	if((conn = accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen))<0)
		ERR_EXIT("accept");
 
	pid_t pid;//父進程用來獲取數據,子進程用來發送數據
	while(1)
        {
                if((conn = accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen)) < 0)
                        ERR_EXIT("accept");
                printf("ip=%s,port=%d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
                pid = fork();
                if(pid == -1)
                        ERR_EXIT("fork");
                if(pid == 0)
                {
                        close(listenfd);
                        do_service(conn);
                        exit(EXIT_SUCCESS);
                }
                else
                        close(conn);
        }
        return 0;

}
  • echocli.c
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
 
#define ERR_EXIT(m)				\
	do					\
	{					\
		perror(m);			\
		exit(EXIT_FAILURE);		\
	}while(0)

ssize_t readn(int fd, void *buf, size_t count)
{
        size_t nleft = count;
        ssize_t nread;
        char *bufp = (char*)buf;

        while(nleft > 0)
        {
                if((nread = read(fd, bufp, nleft)) < 0)
                {
                        if(errno == EINTR)
                                continue;
                        return -1;
                }
                else if(nread == 0)
                        return count - nleft;

                bufp += nread;
                nleft -= nread;
        }

        return count;
}

ssize_t writen(int fd, const void *buf, size_t count)
{
        size_t nleft = count;
        ssize_t nwritten;
        char *bufp = (char*)buf;

        while(nleft > 0)
        {
                if((nwritten = write(fd, bufp, nleft)) < 0)
                {
                        if(errno == EINTR)
                                continue;
                        return -1;
                }
                else if(nwritten == 0)
                        continue;

                bufp += nwritten;
                nleft -= nwritten;
        }

        return count;
}

ssize_t recv_peek(int sockfd, void *buf, size_t len)//與原型相比去掉了flags
{//可以從套接口接收數據,可以指定不將緩衝區數據移除
	while(1)
	{
		int ret = recv(sockfd, buf, len, MSG_PEEK);
		if(ret == -1 && errno == EINTR)
			continue;
		return ret;
	}
	
}

//暗含讀取方法,標準是一個個讀取,但效率太低,因此用偷窺的方法
//recv_peek不可重入
ssize_t readline(int sockfd, void *buf, size_t maxline)
{
	int ret;
	int nread;
	char *bufp = buf;
	int nleft = maxline;
	while(1)
	{
		ret = recv_peek(sockfd, bufp, nleft);
		if(ret < 0)
			return ret;
		else if(ret == 0)
			return ret;//對方關閉了套接口

		nread = ret;
		int i;
		for(i = 0; i < nread; i++)//最簡單情況,有一條消息,我們讀走
		{
			if(bufp[i] == '\n')
			{
				ret = readn(sockfd, bufp, i + 1);
				if(ret != i+1)
					exit(EXIT_FAILURE);
				return ret;
			}
		}
		if(nread > nleft)
			exit(EXIT_FAILURE);

		nleft -= nread;
		ret = readn(sockfd, bufp, nread);
		if(ret != nread)
			exit(EXIT_FAILURE);

		bufp += nread;
	}
	return -1;

}


int main(void)
{
	int sock;//創建一個套接字,與文件描述符相似。
	if((sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
	{//創建套接字,成功返回套接字,失敗返回-1
		ERR_EXIT("socket");
	}
	//地址初始化
	struct sockaddr_in servaddr;//定義IPV4套接字地址
	memset(&servaddr,0,sizeof(servaddr));//初始化地址
	servaddr.sin_family = AF_INET;	//定義地址家族
	servaddr.sin_port = htons(5188);//定義端口號
	//servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//定義IP地址INTADDR_ANY這個的意思是本機全部地址,但服務器和客戶端一般不在一臺主機
	//第二種方法
	servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");//定義IP地址	

	if(connect(sock,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0)//連接函數
	{
		ERR_EXIT("connect");
	}

	struct sockaddr_in localaddr;
	socklen_t addrlen = sizeof(localaddr);
	if(getsockname(sock, (struct sockaddr*)&localaddr, &addrlen) < 0)
		ERR_EXIT("getsockname");
	printf("ip=%s,port=%d\n", inet_ntoa(localaddr.sin_addr),ntohs(localaddr.sin_port));

	char sendbuf[1024] = {0};
	char recvbuf[1024] = {0};
	while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)//通信細節
	{
		writen(sock, sendbuf, strlen(sendbuf));
		int ret = readline(sock, &recvbuf, sizeof(recvbuf));
		if(ret == -1)
			ERR_EXIT("readline");
		else if(ret == 0)
                {
                        printf("client_close\n");
                        break;

                }
		
		fputs(recvbuf, stdout);
		memset(sendbuf, 0, sizeof(sendbuf));
		memset(recvbuf, 0, sizeof(recvbuf));
	}
	close(sock);
	
	return 0;
 
}

getsockname

  • getsockname
	struct sockaddr_in localaddr;
	socklen_t addrlen = sizeof(localaddr);
	if(getsockname(sock, (struct sockaddr*)&localaddr, &addrlen) < 0)
		ERR_EXIT("getsockname");
	printf("ip=%s,port=%d\n", inet_ntoa(localaddr.sin_addr),ntohs(localaddr.sin_port));

gethostname、gethostbyname

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

#define ERR_EXIT(m)				\
	do					        \
	{					        \
		perror(m);		    	\
		exit(EXIT_FAILURE);		\
	}while(0)

int getlocalip(char *ip)
{
	char host[100] = {0};
	if(gethostname(host, sizeof(host)) < 0)
		return -1;
	struct hostent *hp;
	if((hp = gethostbyname(host)) == NULL)
		return -1;
	strcpy(ip, inet_ntoa(*(struct in_addr*)hp->h_addr));
	return 0;
}

int main(void)
{
	char host[100] = {0};
	if(gethostname(host, sizeof(host)) < 0)
		ERR_EXIT("gethostname");
	struct hostent *hp;
	if((hp = gethostbyname(host)) == NULL)
		ERR_EXIT("gethostbyname");

	int i = 0;
	while((hp->h_addr_list[i]) != NULL)
	{
		printf("%s\n", inet_ntoa(*(struct in_addr*)hp->h_addr_list[i]));
		i++; 
	}
	
	char ip[16] = {0};
	getlocalip(ip);
	printf("localip=%s\n", ip); 
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章