linux TCP socket 实例

0:TCP/IP协议栈与数据包封装

CP/IP网络协议栈分为应用层(Application)、传输层(Transport)、网络层(Network)和链路层(Link)四层。


不同的协议层对数据包有不同的称谓,在传输层叫做段(segment)在网络层叫做数据报(datagram),在链路层叫做帧(frame)。数据封装成帧后发到传输介质上,到达目的主机后每层协议再剥掉相应的首部,最后将应用层数据交给应用程序处理。。IP地址是标识网络中不同主机的地址,而端口号就是同一台主机上标识不同进程的地址,IP地址和端口号合起来标识网络中唯一的进程。

1:基于TCP服务端和客户端系统调用


由于客户端不需要固定的端口号,因此不必调用bind(),客户端的端口号由内核自动分配。注意,客户端不是不允许调用bind(),只是没有必要调用bind()固定一个端口号,服务器也不是必须调用bind(),但如果服务器不调用bind(),内核会自动给服务器分配监听端口,每次启动服务器时端口号都不一样,客户端要连接服务器就会遇到麻烦。

2:socket 地址说明

Generic Socket Address Structures通用socket address
struct sockaddr {
	sa_family_t sa_family; /* Address family (AF_* constant) */
	char sa_data[14]; /* Socket address (size varies
	according to socket domain) */
};
struct sockaddr_in { /* IPv4 socket address */
	sa_family_t sin_family; /* Address family (AF_INET) */
	in_port_t sin_port; /* Port number */
	struct in_addr sin_addr; /* IPv4 address */
	unsigned char __pad[X]; /* Pad to size of 'sockaddr'
	structure (16 bytes) */
};

struct in_addr { /* IPv4 4-byte address */
	in_addr_t s_addr; /* Unsigned 32-bit integer */
};

在linux socket编程中使用的socket地址都为通用格式即:struct sockaddr
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);Returns 0 on success, or –1 on error
对于IPv4,family参数指定为AF_INET。
对于TCP协议,type参数指定为SOCK_STREAM,表示面向流的传输协议。
对于UDP协议,type参数指定为SOCK_DGRAM,表示面向数据报的传输协议。
protocol参数的介绍从略,指定为0即可。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); Returns 0 on success, or –1 on error
int listen(int sockfd, int backlog); Returns 0 on success, or –1 on error 
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); Returns file descriptor on success, or –1 on error
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);Returns 0 on success, or –1 on error 
比如在bind(),accept(),connect()中的函数中都使用的是struct sockaddr地址,但是在实际的使用中使用的是:struct sockaddr_in地址。因此代码中常用的地址格式定义如下:
struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_addr.s_addr = inet_addr("192.168.100.244");
servaddr.sin_port = htons(SERV_PORT);
struct sockaddr_in中的成员struct in_addr sin_addr表示32位的IP地址。实际中多数使用点分十进制的字符串表示IP地址,以下函数可以实现字符串表示和in_addr表示之间转换。
字符串转in_addr的函数:
#include <arpa/inet.h>
int inet_aton(const char *strptr, struct in_addr *addrptr);
in_addr_t inet_addr(const char *strptr);
int inet_pton(int family, const char *strptr, void *addrptr);
in_addr转字符串的函数:
char *inet_ntoa(struct in_addr inaddr);
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);

3:TCP服务端和客户端代码框架。

服务端:通过server ip和port 返回socketID
int getServerSokcketId(int argc, char *argv[])
{
	struct sockaddr_in servaddr;
	int listenfd,optval = 1;;
	if (argc != 3)
	{
		fputs("usage: ./server serverIp serverPort\n", stderr);
		exit(1);
	}
	listenfd = socket(AF_INET, SOCK_STREAM, 0);
	if(listenfd == -1)
	{
    close(listenfd);
		perror("create server socket ....");
		exit(0);
	}
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
//servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_addr.s_addr = inet_addr(argv[1]);
	servaddr.sin_port = htons(atoi(argv[2]));
  if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &optval,sizeof(optval)) == -1) 
  {
        close(listenfd);
				perror("server socket  setsockopt....");
        return -1;
  }
	if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
	{
		perror("server bind ....");
    close(listenfd);
		exit(0);
	}
	if(listen(listenfd, 20) == -1)
	{
		perror("server listen ....");
    close(listenfd);
		exit(0);
	}
	printf("Accepting connections ...\n");
	return listenfd;

}
main函数调用,同时完成对客户端的请求处理。
int main(int argc, char *argv[])
{
		struct sockaddr_in  cliaddr;
		socklen_t cliaddr_len;
		int listenfd, acceptFd;
    listenfd = getServerSokcketId(argc,argv);
		while(1)
		{
			cliaddr_len = sizeof(cliaddr);
			acceptFd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);  /* Wait for connection */
			if (acceptFd == -1)
			{
				syslog(LOG_ERR, "Failure in accept(): %s",strerror(errno));
				continue;           /* Try next */
			}
			else
			{
				printf("received from %s at PORT %d\n",inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),ntohs(cliaddr.sin_port));
			}
#if 0
			switch (fork()) //使用多进程处理用户的请求
			{       /* Create child for each client */
			case -1:
			printf("------------------------------fork() return error\n");
				syslog(LOG_ERR, "111Can't create child (%s)",strerror(errno));
				close(acceptFd);         /* Give up on this client */
				break;              /* May be temporary; try next client */

			case 0:                 /* Child */
				close(listenfd);         /* Don't need copy of listening socket */
				handleRequest(acceptFd);
				printf("----handleRequest execute over--------------------\n");
				_exit(EXIT_SUCCESS);

			default:                /* Parent */
				close(acceptFd);         /* Don't need copy of connected socket */
				break;              /* Loop to accept next connection */
			}
	#endif
			clientProcess(acceptFd);//使用多线程处理客户端的请求。
		 }
}
其中函数clientProcess(acceptFd);定义如下:
void *thr_fn1(int cfd)
{
	ssize_t numRead,numSocketRead,numSocketWrite;
	char message[1000];
	while(1)
	{
		if((numSocketRead = read(cfd, &message, sizeof(message))) > 0)
		{			
			//此处对收到的message进行处理
			numSocketWrite=write(cfd, &message, sizeof(message));
			if ((numSocketWrite != sizeof(message)) || numSocketWrite == -1) 
			{
					syslog(LOG_ERR, "write() failed: %s", strerror(errno));
					return(EXIT_FAILURE);
			}	
		}
		f(numSocketRead <= 0)
		{
			printf("process pid:%d exit\n",getpid());
			close(cfd);
			syslog(LOG_ERR, "Error from read(): %s", strerror(errno));
			return(EXIT_FAILURE);
		}			
	}	
}
void clientProcess(int cfd)
{
  	pthread_t tid1;
	pthread_create(&tid1, NULL, thr_fn1, cfd);
}
客户端代码
以下函数返回客户端socketID跟服务端类似。
int getClietnSocketId(int argc, char *argv[])
{
	struct sockaddr_in servaddr;
	char buf[MAXLINE];
	int sockfd, n;
	if (argc != 3)
	{
		fputs("usage: ./client serverIp serverPort\n", stderr);
		exit(1);
	}
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd == -1)
	{
		perror("create socket....\n");
	}
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(atoi(argv[2]));
	inet_aton(argv[1], &servaddr.sin_addr.s_addr);
	if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr.s_addr) <= 0)
	{
		printf("set ip address error!\n");
		exit(0);
	}
//inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr.s_addr);
	if(connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))==-1)
	{
		perror("connect....");
		exit(0);
	}
	return sockfd;

}
main函数
int main(int argc, char *argv[])
{
		int sfd=0,numRead;
		char  parentMsg[1000];
    char  message[1000];
    char buf[BUF_SIZE];
    memset(&message,0,sizeof(message));
    memset(&parentMsg,0,sizeof(parentMsg));
		sfd= getClietnSocketId(argc,argv);
    if (sfd == -1)
  	{
  		errExit("inetConnect");
  	}
    switch (fork()) 
    {
	    case -1:
	        errExit("fork");
	    case 0:             /* Child: read server's response, echo on stdout */
		    memset(&message,0,sizeof(message));
	        for (;;) {
	        	//memset(&message,0,sizeof(struct fileMessage));
		   mutex = pthread_mutex_lock(&mtx);
		   if (mutex != 0)
		      printf("pthread_mutex_lock");
	            if((numRead = read(sfd, &message, sizeof(message)))>0)
	            {

	            }
		   mutex = pthread_mutex_unlock(&mtx);
		   if (mutex!= 0)
		      printf("pthread_mutex_unlock");
	            if (numRead <= 0)                   /* Exit on EOF or error */
	            {
	            	printf("server has closed!..%s\n",strerror(errno));
	            	break;
	            }
	        }
	        printf("-------------fork exit, message: %s----------------------------------\n",strerror(errno));
	        _exit(EXIT_SUCCESS);
	    default:            /* Parent: write contents of stdin to socket */	
	    			while(1)
	    			{ 				
	    				if((numRead = read(STDIN_FILENO, buf, BUF_SIZE))>0)
    					{   								    			
				    		if((write(sfd, &parentMsg, sizeof(parentMsg))) != sizeof(parentMsg))
			           		{
				           		fatal("write() failed");
			           			close(sfd)
			           		}    							
    					}  				
	    			}    	
        }
        exit(EXIT_SUCCESS);
    }
}

4:执行结果







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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

家有工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值