您的位置:首页 > 理论基础 > 计算机网络

socket 编程 TCP 实现简单聊天功能

2013-06-02 15:42 881 查看


各个主要函数的功能:

1、socket

int socket(int domain, int type,int protocol)



domain:说明我们网络程序所在的主机采用的通讯协族(AF_UNIX 和 AF_INET 等).

AF_UNIX 只能够用于单一的 Unix 系统进程间通信,而 AF_INET 是针对 Internet 的,因而可以允

许在远程 主机之间通信(当我们 man socket 时发现 domain 可选项是 PF_*而不是 AF_*,因

为 glibc 是 posix 的实现 所以用 PF 代替了 AF,不过我们都可以使用的).



type:我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM 等) SOCK_STREAM

表明我们用的是 TCP 协议,这样会提供按顺序的,可靠,双向,面向连接的比特流. SOCK_DGRAM

表明我们用的是 UDP 协议,这样只会提供定长的,不可靠,无连接的通信.



protocol:由于我们指定了 type,所以这个地方我们一般只要用 0 来代替就可以了

socket 为网络通讯做基本的准备.成功时返回文件描述符,失败时返回-1,看 errno 可知道出错

的详细情况.





2、bind

int bind(int sockfd, struct sockaddr *my_addr, int addrlen)



sockfd:是由 socket 调用返回的文件描述符.



addrlen:是 sockaddr 结构的长度.



my_addr:是一个指向 sockaddr 的指针. 在中有 sockaddr 的定义



struct sockaddr{

unisgned short as_family;

char sa_data[14];

};



不过由于系统的兼容性, 我们一般不用这个头文件, 而使用另外一个结构(struct sockaddr_in) 来代替.在中有 sockaddr_in 的定义

struct sockaddr_in{

unsigned short sin_family;

unsigned short int sin_port;

struct in_addr sin_addr;

unsigned char sin_zero[8];



我们主要使用 Internet 所以 sin_family 一般为 AF_INET,sin_addr 设置为 INADDR_ANY

表示可以和任何的主机通信,sin_port 是我们要监听的端口号.sin_zero[8]是用来填充的. bind

将本地的端口同 socket 返回的文件描述符捆绑在一起.成功是返回 0,失败的情况和 socket 一





3、listen

int listen(int sockfd,int backlog)



sockfd:是 bind 后的文件描述符.



backlog:设置请求排队的最大长度.当有多个客户端程序和服务端相连时, 使用这个表示

可以介绍的排队长度. listen 函数将 bind 的文件描述符变为监听套接字.返回的情况和 bind 一

样.





4、 accept

int accept(int sockfd, struct sockaddr *addr,int *addrlen)



sockfd:是 listen 后的文件描述符.



addr,addrlen 是用来给客户端的程序填写的,服务器端只要传递指针就可以了. bind,listen

和 accept 是服务器端用的函数,accept 调用时,服务器端的程序会一直阻塞到有一个客户程序

发出了连接. accept 成功时返回最后的服务器端的文件描述符,这个时候服务器端可以向该描

述符写信息了. 失败时返回-1





5、 connect

int connect(int sockfd, struct sockaddr * serv_addr,int addrlen)

sockfd:socket 返回的文件描述符.



serv_addr:储存了服务器端的连接信息.其中 sin_add 是服务端的地址



addrlen:serv_addr 的长度



connect 函数是客户端用来同服务端连接的.成功时返回 0,sockfd 是同服务端通讯的文件

描述符 失败时返回-1.

6、

gethostbyname 和 gethostbyaddr 都涉及到一个 hostent 的结构体,如下所示:



Struct hostent{

char *h_name;/*正式主机名*/

char **h_aliases;/*主机别名*/

int h_addrtype;/*地址类型*/

int h_length;/*地址长度*/

char **h_addr_list;/*指向 IPv4或 IPv6的地址指针数组*/

}

所需头文件 #include <netdb.h>

函数原型 Struct hostent *gethostbyname(const char *hostname)

函数传入值 Hostname:主机名

函数返回值

成功:
hostent 类型指针

出错:−1

服务端源码:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h> 
#include <string.h> 
#include <netdb.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <sys/socket.h>

int main(int argc, char *argv[])
{
	int sockfd,new_fd; 
	int sockaddr_size;
	int ret;
	int len;
	struct sockaddr_in server_addr;   //结构体sockaddr_in 和 aockaddr 是兼容的
	struct sockaddr_in client_addr;   
	int sin_size,portnumber;   
	char buf[1024];

	fd_set rfds;
	struct timeval tv;
	int maxfd;
	FD_ZERO(&rfds);
    FD_SET(STDIN_FILENO, &rfds);

	if((portnumber = atoi(argv[1]))<0)
	{
		printf("port number error, will exit\n");
		exit(1);
	}
	/*  服务器端开始建立 socket 描述符  */     
	if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)    //AF_INET也可以用PF_INET代替,表明针对錓neternet,可以建立远程连接。SOCK_STREAM表明使用的是TCP协议   
	{   													//而 SOCK_DGRAM 表明我们用的是 UDP 协议
		printf("socket, will exit\n");
		close(sockfd);
		exit(1);  
	}
	/*  服务器端填充  sockaddr 结构    */     
	bzero(&server_addr,sizeof(struct sockaddr_in));   
	server_addr.sin_family=AF_INET;   
	server_addr.sin_addr.s_addr=htonl(INADDR_ANY);   //标识可以和任何主机通信
	server_addr.sin_port=htons(portnumber);  

	/*  捆绑 sockfd 描述符    */     
	if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)   
	{   
        printf("bind error, will exit\n");   
		close(sockfd);
        exit(1);   
	}
	/*  监听 sockfd 描述符    */   
	if(listen(sockfd,5)==-1)  //设置客户端请求排队的最大长度,允许最大5个 
	{	   
        printf("listen, will exit\n"); 
		close(sockfd);		
		exit(1);   
	}

	while(1)
	{
		 /*  服务器阻塞,直到客户程序建立连接    */   
		printf("Will go to talk, wait connecting...\n");
		sockaddr_size = sizeof(struct sockaddr);
		//int accept(int sockfd, struct sockaddr *addr,int *addrlen)    
		if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sockaddr_size))==-1)  
		{
			printf("accept error, will exit\n");
			close(sockfd);
			exit(1);
		}
		else
		{
            printf("server: get connection from %s, port %d, socket %d\n",
                   (char *)inet_ntoa(client_addr.sin_addr),
				   ntohs(client_addr.sin_port),
				   new_fd);
		}
		//进行会话
		while(1)
		{

			FD_ZERO(&rfds);
			FD_SET(STDIN_FILENO, &rfds);
			FD_SET(new_fd, &rfds);
			//maxfd = new_fd > STDIN_FILENO ? newfd :STDIN_FILENO;
			//maxfd = maxfd + 1;
			maxfd = new_fd + 1;
			
			tv.tv_sec = 1;
			tv.tv_usec = 0;//设置等待时间
			ret = select(maxfd, &rfds, NULL, NULL, &tv);
			if(ret<0)
			{
				printf("select error, process will eixt\n");
				exit(0);
			}
			else if(FD_ISSET(STDIN_FILENO, &rfds))//测试是否有数据
			{
					bzero(buf,1024);
					//printf("Server	:");
					fgets(buf, 1024, stdin);
					if (!strncasecmp(buf, "quit", 4)) 
					{
						printf("current talk exit\n");
						FD_CLR(new_fd, &rfds); 
						close(new_fd);
						break;
					}
					//printf("Server	:%s\n",buf);
					//  int recv(int sockfd,void *buf,int len,int flags)   
					//  int send(int sockfd,void *buf,int len,int flags)   
					//最后一个参数可以是0,也可以是
/*					  _______________________________________________________________   
|    MSG_DONTROUTE                |    不查找路由表                                                  |   
|    MSG_OOB                            |    接受或者发送带外数据                                    |   
|    MSG_PEEK                          |    查看数据,并不从系统缓冲区移走数据            |   
|    MSG_WAITALL                    |    等待所有数据                                                  |   
|--------------------------------------------------------------| 
*/
					len = send(new_fd,buf,strlen(buf)-1,0);
					if(len < 0)
					{
						printf("send fail\n");
					}
					
					
				
			}
			else if(FD_ISSET(new_fd, &rfds))
			{
			
				bzero(buf,1024);
				len = recv(new_fd,buf,strlen(buf)-1,0);
				if(len < 0)
				{
					printf("receive fail\n");
				}
				if(len == 0)//当客户端终止运行的时候,会不断检测到sockfd的状态变化,但是收到的信息长度为0
				{
					printf("Client has already exit, will exit\n");
					FD_CLR(new_fd, &rfds); 
					close(new_fd);
					break;
				
				}
				if (!strncasecmp(buf, "quit", 4)) 
				{
					printf("current talk exit\n");
					FD_CLR(new_fd, &rfds); 
					close(new_fd);
					break;
				}
				buf[len+1]='\0';
				printf("		Client	:%s\n",buf);
			
			}
		
		}
	
	}
	
}


客户端:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h> 
#include <string.h> 
#include <netdb.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <sys/socket.h>

int main(int argc, char *argv[])
{
	int sockfd,new_fd; 
	int sockaddr_size;
	int ret;
	int len;
	struct sockaddr_in server_addr;   //结构体sockaddr_in 和 aockaddr 是兼容的
	 struct hostent *host;
	int sin_size,portnumber;   
	char buf[1024];

	fd_set rfds;
	struct timeval tv;
	int maxfd;
	FD_ZERO(&rfds);
    FD_SET(STDIN_FILENO, &rfds);

	 if((host=gethostbyname(argv[1])) == 0)
	{
		printf("server ip %s error\n",argv[1]);
		exit(1);
	}
	
	if((portnumber = atoi(argv[2]))<0)
	{
		printf("port number error, will exit\n");
		exit(1);
	}
	/*  客户程序开始建立  sockfd 描述符    */       
	if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)    //AF_INET也可以用PF_INET代替,表明针对錓neternet,可以建立远程连接。SOCK_STREAM表明使用的是TCP协议   
	{   													//而 SOCK_DGRAM 表明我们用的是 UDP 协议
		printf("socket, will exit\n");
		exit(1);  
	}
	/*  客户程序填充服务端的资料              */      
	bzero(&server_addr,sizeof(struct sockaddr_in));   
	server_addr.sin_family=AF_INET;   
	server_addr.sin_port=htons(portnumber);  
	server_addr.sin_addr=*((struct in_addr *)host->h_addr); 

	/*  客户程序发起连接请求                  */     
	if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)   
	{
		printf("connect will exit\n");
		exit(1);  
	}
	printf("start talk...\n");
	
	FD_SET(sockfd, &rfds);
		//进行会话
		while(1)
		{

			FD_ZERO(&rfds);
			FD_SET(STDIN_FILENO, &rfds);
			FD_SET(sockfd, &rfds);
			
			//maxfd = new_fd > STDIN_FILENO ? newfd :STDIN_FILENO;
			//maxfd = maxfd + 1;
			maxfd = sockfd + 1;
			
			tv.tv_sec = 1;
			tv.tv_usec = 0;//设置等待时间
			ret = select(maxfd, &rfds, NULL, NULL, &tv);
			if(ret<0)
			{
				printf("select error, process will eixt\n");
				exit(0);
			}
			else if(FD_ISSET(STDIN_FILENO, &rfds))//测试是否有数据
			{
					bzero(buf,1024);
					//printf("Client	:");
					fgets(buf, 1024, stdin);
					if (!strncasecmp(buf, "quit", 4)) 
					{
						printf("current talk exit\n");
						FD_CLR(sockfd, &rfds); 
						close(sockfd);
						break;
					}
					//printf("Client	:%s\n",buf);
					//  int recv(int sockfd,void *buf,int len,int flags)   
					//  int send(int sockfd,void *buf,int len,int flags)   
					//最后一个参数可以是0,也可以是
/*					  _______________________________________________________________   
|    MSG_DONTROUTE                |    不查找路由表                                                  |   
|    MSG_OOB                            |    接受或者发送带外数据                                    |   
|    MSG_PEEK                          |    查看数据,并不从系统缓冲区移走数据            |   
|    MSG_WAITALL                    |    等待所有数据                                                  |   
|--------------------------------------------------------------| 
*/
					len = send(sockfd,buf,strlen(buf)-1,0);
					if(len < 0)
					{
						printf("send fail\n");
					}
					
					
				
			}
			else if(FD_ISSET(sockfd, &rfds))
			{
			
				bzero(buf,1024);
				len = recv(sockfd,buf,strlen(buf)-1,0);
				if(len < 0)
				{
					printf("receive fail\n");
				}
				if(len == 0)//当服务器终止运行的时候,会不断检测到sockfd的状态变化,但是收到的信息长度为0
				{
					printf("Server has already exit, will exit\n");
					FD_CLR(sockfd, &rfds); 
					close(sockfd);
					break;
				
				}
				if (!strncasecmp(buf, "quit", 4)) 
				{
					printf("current talk exit\n");
					FD_CLR(sockfd, &rfds); 
					close(sockfd);
					break;
				}
				buf[len+1]='\0';
				printf("		Server:	%s\n",buf);
			
			}
		
		}
	

	
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: