TCP程序设计

Socket和基本结构体

Linux中的网络编程通过Socket(套接字)实现,Socket是一种文件描述符。

Socket有三种类型:

流式套接字(SOCK_STREAM):使用TCP协议。

数据报套接字(SOCK_DGRAM):使用UDP协议。

原始套接字(SOCK_RAW):使用IP协议,主要用于新的网络协议的测试等。

网络地址

在socket程序设计当中,struct sockaddr用于记录网络地址:

struct sockaddr

{

    u_short sa_family;

    char sa_data[14];

}

sa_family:协议族,采用“AF_XXX”的形式,如AF_INET(IP协议族)。

sa_data:14字节的特定协议地址。

地址结构

struct sockaddr_in

{

    short int sa_family;   /*协议族*/

    unsigned short int sin_port;  /*端口号*/

    struct in_addr sin_addr;        /*协议特定地址*/

    unsigned char sin_zero[8];     /*填0*/

}

注意:在一般应用中,一般不会使用sockaddr,一般使用sockaddr_in,因为其操作简单。

typedef struct in_addr

{

    union

    {

        struct

           {

                unsigned char s_b1,s_b2,s_b3,s_b4;

           }S_un_b;

           struct

          {

                unsigned short s_w1,s_w2; 

           }S_un_w;

         unsigned long S_addr;   /*通常在联合体当中使用这个结构体,此为32位无符号整数来记录IP地址(假设为IP协议)

     }S_un;

}

地址转换

IP地址通常由数字加点(192.168.0.1)的形式表示,而在struct in_addr中使用的IP地址是由32位的整数表示的,为了转换我们可以使用以下两个函数:

int inet_aton(const char *cp,struct in_addr *inp)

char *inet_ntoa(struct in_addr *in)

函数里面a代表ascii(字符),n代表network。

inet_aton是将a.b.c.d形式的IP转换为32位的IP,存储在inp指针里面。inet_ntoa是将32位IP地址转换为a.b.c.d的格式。

字节序转换

不同类型的CPU对变量的字节存储顺序可能不同:(低字节先传输为big endian),有的系统是高位在前,低位在后,而有的系统是低位在前,高位在后,而网络传输的数据顺序是一定要统一的。所以当内部字节顺序和网络字节顺序不同是,必须要进行转换。

字节序转换函数

htons

把unsigned short 类型从主机序转换到网络序

htonl

把unsigned long类型从主机序转换到网络序

ntohs

把unsigned short 类型从网络序转换到主机序

ntohl

把unsigned long类型从网络序转换到主机序

IP与主机名:

在网络中标识一台主机可以用IP地址,也可以使用主机名。

struct hostent *gethostbyname(const char *hostname)

struct hostent

{

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

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

    int h_addrtype;             /*主机的地址类型 AF_INET*/

    int h_length;                  /*主机的地址长度*/

    char **h_addr_list;        /*主机的IP地址列表*/

}

#define h_addr h_addr_list[0];   /*主机的第一个IP地址*/

基于TCP的网络编程

基于TCP-服务器

1 创建一个socket,用函数socket();

2 绑定IP地址、端口等信息到socket上,用函数bind();

3 设置最大的允许的连接数,用listen();

4 等待客户端的连接请求,用函数accept(),如果没有连接请求则程序阻塞在accept处。

5 收发数据,用函数send()和recv(),或者read()和write();

6 关闭网络连接;

基于TCP-客户端

1 创建一个socket,用函数socket();

2 设置要连接的服务器的IP地址和端口属性

3 连接服务器,用函数connect();

4 收发数据,用函数send()和recv(),或者read()和write();

5 关闭网络连接;

TCP通信实例如下:

1 TCP服务器程序:

/**********************************************************
*实验要求:   建立基于TCP协议的服务器与客户端的连接。客户端向服务器发送
*           字符串,服务器将收到的字符串打印出来。
*功能描述:   根据套接字建立TCP连接的过程,创建服务端程序,并在服务端等
*           待接收客户端的数据,并打印到终端上。
*日    期:   2010-9-17
*作    者:   国嵌
**********************************************************/
#include <stdlib.h> 
#include <stdio.h> 
#include <errno.h> 
#include <string.h> 
#include <netdb.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <sys/socket.h> 

#define portnumber 3333

/*
* 程序入口
* */
int main(int argc, char *argv[]) 
{ 
	int sockfd,new_fd; 
	struct sockaddr_in server_addr; 
	struct sockaddr_in client_addr; 
	int sin_size; 
	int nbytes;
	char buffer[1024];
	

	/* 服务器端开始建立sockfd描述符 */ 
	if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) // AF_INET:IPV4;SOCK_STREAM:TCP
	{ 
		fprintf(stderr,"Socket error:%s\n\a",strerror(errno)); 
		exit(1); 
	} 

	/* 服务器端填充 sockaddr结构 */ 
	bzero(&server_addr,sizeof(struct sockaddr_in)); // 初始化,置0
	server_addr.sin_family=AF_INET;                 // Internet
	server_addr.sin_addr.s_addr=htonl(INADDR_ANY);  // (将本机器上的long数据转化为网络上的long数据)服务器程序能运行在任何ip的主机上  //INADDR_ANY 表示主机可以是任意IP地址,即服务器程序可以绑定到所有的IP上
	//server_addr.sin_addr.s_addr=inet_addr("192.168.1.1");  //用于绑定到一个固定IP,inet_addr用于把数字加格式的ip转化为整形ip
	server_addr.sin_port=htons(portnumber);         // (将本机器上的short数据转化为网络上的short数据)端口号
	
	/* 捆绑sockfd描述符到IP地址 */ 
	if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1) 
	{ 
		fprintf(stderr,"Bind error:%s\n\a",strerror(errno)); 
		exit(1); 
	} 

	/* 设置允许连接的最大客户端数 */ 
	if(listen(sockfd,5)==-1) 
	{ 
		fprintf(stderr,"Listen error:%s\n\a",strerror(errno)); 
		exit(1); 
	} 

	while(1) 
	{ 
		/* 服务器阻塞,直到客户程序建立连接 */ 
		sin_size=sizeof(struct sockaddr_in); 
		if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size))==-1) 
		{ 
			fprintf(stderr,"Accept error:%s\n\a",strerror(errno)); 
			exit(1); 
		} 

		fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr)); // 将网络地址转换成.字符串
		if((nbytes=read(new_fd,buffer,1024))==-1) 
		{ 
			fprintf(stderr,"Read Error:%s\n",strerror(errno)); 
			exit(1); 
		} 		
		buffer[nbytes]='\0';
		printf("Server received %s\n",buffer);
		/* 这个通讯已经结束 */ 
		close(new_fd); 
		/* 循环下一个 */ 
	} 

	/* 结束通讯 */ 
	close(sockfd); 
	exit(0); 
}


2 TCP客户端程序:

/**********************************************************
*实验要求:   建立基于TCP协议的服务器与客户端的连接。客户端向服务器发送
*           字符串,服务器将收到的字符串打印出来。
*功能描述:   根据套接字建立TCP连接的过程,创建客户端程序,并在与服务端
*           建立连接后,向其发送指定字符串。
*日    期:   2010-9-17
*作    者:   国嵌
**********************************************************/
#include <stdlib.h> 
#include <stdio.h> 
#include <errno.h> 
#include <string.h> 
#include <netdb.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <sys/socket.h> 

#define portnumber 3333

/*
* 程序入口
* */
int main(int argc, char *argv[]) 
{ 
	int sockfd; 
	char buffer[1024]; 
	struct sockaddr_in server_addr; 
	struct hostent *host; 

	if(argc!=2) 
	{ 
		fprintf(stderr,"Usage:%s hostname \a\n",argv[0]); 
		exit(1); 
	} 

    /* 使用hostname查询host 名字 */
	if((host=gethostbyname(argv[1]))==NULL) 
	{ 
		fprintf(stderr,"Gethostname error\n"); 
		exit(1); 
	} 

	/* 客户程序开始建立 sockfd描述符 */ 
	if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) // AF_INET:Internet;SOCK_STREAM:TCP
	{ 
		fprintf(stderr,"Socket Error:%s\a\n",strerror(errno)); 
		exit(1); 
	} 

	/* 客户程序填充服务端的资料 */ 
	bzero(&server_addr,sizeof(server_addr)); // 初始化,置0
	server_addr.sin_family=AF_INET;          // IPV4
	server_addr.sin_port=htons(portnumber);  // (将本机器上的short数据转化为网络上的short数据)端口号
	server_addr.sin_addr=*((struct in_addr *)host->h_addr); // IP地址
	
	/* 客户程序发起连接请求 */ 
	if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1) 
	{ 
		fprintf(stderr,"Connect Error:%s\a\n",strerror(errno)); 
		exit(1); 
	} 

	/* 连接成功了 */ 
	printf("Please input char:\n");
	
	/* 发送数据 */
	fgets(buffer,1024,stdin); 
	write(sockfd,buffer,strlen(buffer)); 
	/* 结束通讯 */ 
	close(sockfd); 
	exit(0); 
}


运行结果:

先运行TCP服务器程序

[root@localhost TCP]# ./tcp_server

/****阻塞处*****/

发生阻塞,阻塞发生在tcp_server.c中的accept函数处

然后运行TCP客户端程序重新开一个终端:

[root@localhost TCP]# ./tcp_client 192.168.0.102                       /*192.168.0.102为服务器地址*/

Please input char:

一旦连接后,主机端发生变化,如下:

[root@localhost TCP]# ./tcp_server

Server get connection from 192.168.0.102

/****阻塞处*****/

同时发生阻塞,阻塞发生在TCP_server.c中的read函数中,由于此时客户端没有向服务器发送数据

接下来我们从客户端发送数据:

[root@localhost TCP]# ./tcp_client 192.168.0.102                      

Please input char:

1234567

则服务器端也发生相应变化如下:

[root@localhost TCP]# ./tcp_server

Server get connection from 192.168.0.102

Server received 1234567

/*****阻塞处****/

又发生阻塞,阻塞在accept处,因为此时tcp_server.c中的while循环一次结束,关闭连接,又循环到accept处。依照此方法不断发送与接收数据。

 

此文章按照国嵌视频  国嵌视频\光盘3\课程2(嵌入式LINUX应用开发班)\第7天(网络编程)所撰写,此处予以感谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值