20.Linux:网络编程(TCP协议-简单版本):client和server(原创)

需要注意的地方:

1.两端的socket函数通信类型(IPv4还是IPv6),通信协议(TCP还是UDP)都要一致

2.两端对服务器的设置都要一致,对于客户端而言,这些设置信息是用来给connect函数提供连接信息的

3.特别注意:在服务器端发送和接收信息,send和recv函数的套接字必须用客户端的,也就是accept函数的返回值

下面是程序:

客户端:

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

#define	SERVPORT 3336
#define MAXDATASIZE 100

main(int argc,char *argv[])
{
	int sendbytes,recvbytes;
       int sockfd,serv_fd;
	char buf[MAXDATASIZE];
	struct sockaddr_in serv_addr;//这里是设置要连接的服务器的信息,用什么类型地址,服务器端口号多少,地址多少
	struct hostent *host;

	if(argc < 2)
	{
		fprintf(stderr,"Please enter the server's hostname!\n");
		exit(1);
	}

	if (( host = gethostbyname(argv[1])) == NULL)
	{
		perror("gethostbyname");
		exit(1);
	}

	//** creat socket
	if (( sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
	{
		perror("socket");
		exit(1);
	}

    //设置需要连接的服务器的信息
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(SERVPORT);//先将主机字节序转换为网络字节序,再赋值
	serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
	bzero(&(serv_addr.sin_zero),8);//置字节字符串serv_addr.sin_zero的前8个字节为零。

	if ( connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr)) == -1)//serv_addr强制转换为sockaddr
	{
		perror("connect");
		exit(1);
	}

    //向服务器端发送信息,注意send的套接字为自己本身
	if((sendbytes = send(sockfd,"hello",6,0)) == -1)
	{
		perror("send");
		exit(1);
	}

	//接受来自服务器端的信息,注意recv的套接字为自己本身
	if(recvbytes = recv(sockfd,buf,sizeof(buf),0) == -1)
	{
		perror("recv");
		exit(1);
	}
	printf("The client receive: %s\n",buf);

	close(sockfd);
}


服务器端:

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

#define SERVPORT 3336
#define BACKLOG 10
#define	MAX_CONNECTED_NO 10
#define MAXDATASIZE 100

int main()
{
	struct sockaddr_in server_sockaddr,client_sockaddr;
	int sin_size,recvbytes,sendbytes;
	fd_set readfd;
	fd_set writefd;
	int sockfd,client_fd;
	char buf[MAXDATASIZE];

	if (( sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)//此处必须跟客户端保持一致,都用IPv4和TCP
	{
		perror("socket");
		exit(1);
	}
	printf("socket success!sockfd = %d\n",sockfd);

    //服务器本身端口号设置,客户端必须与此处保持一致才能连接上
	server_sockaddr.sin_family = AF_INET;
	server_sockaddr.sin_port = htons(SERVPORT);//端口号与客户端保持一致
	server_sockaddr.sin_addr.s_addr = INADDR_ANY;
	bzero(&(server_sockaddr.sin_zero),8);

	if (bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr)) == -1)//第二个参数这里是本地地址(服务器地址)所在的结构体指针
	{
		perror("bind");
		exit(1);
	}
	printf("bind success!\n");

	if (listen(sockfd,BACKLOG) == -1)//设置最大可以监听的数量
	{
		perror("listen");
		exit(1);
	}
	printf("listening...\n");

	if (( client_fd = accept(sockfd, (struct sockaddr *)&client_sockaddr,&sin_size)) == -1)//这里的客户端地址会被发送过来连接的地址填好(客户端程序的                                                //<span style="font-family: Arial, Helvetica, sans-serif;">服务</span><span style="font-family: Arial, Helvetica, sans-serif;">器地址)</span>
	{
		perror("accept");
		exit(1);
	}

    //服务器端的发送和接收套接字必须都是客户端的---即accept函数的返回值
	if (( recvbytes = recv(client_fd,buf,MAXDATASIZE,0)) == -1)
	{
		perror("recv");
		exit(1);
	}
	printf("received a connection: %s\n",buf);

    //服务器端的发送和接收套接字必须都是客户端的---即accept函数的返回值
	if ( sendbytes = send(client_fd,"server-I receive",17,0) == -1){
		perror("send");
		exit(1);
	}

	close(client_fd);
}


两个重要函数:

1.accept()函数

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

      sockfd,    利用系统调用socket()建立的套接字描述符,通过bind()绑定到一个本地地址(一般为服务器的套接字),并且通过listen()一直在监听连接

     addr,    指向struct sockaddr的指针,该结构用通讯层服务器对等套接字的地址(一般为客户端程序的地址)填写,返回地址addr的确切格式由套接字的地址类别(比如TCP或UDP)决定;若addr为NULL,没有有效地址填写,这种情况下,addrlen也不使用,应该置为NULL;

备注:addr是个指向局部数据结构sockaddr_in的指针,这就是要求接入的套接字(地址和指针)。

     addrlen,    一个值结果参数,调用函数必须初始化为包含addr所指向结构大小的数值,函数返回时包含对等地址(一般为服务器地址)的实际数值;

   accept()用来接受参数s 的socket 连线. 参数s 的socket 必需先经bind()、listen()函数处理过, 

  当有连线进来时accept()会返回一个新的socket 处理代码, 往后的数据传送与读取就是经由新的socket处理, 这个新的socket与原来的socket不一样,原来参数s 的socket 能继续使用accept()来接受新的连线要求. 连线成功时, 参数addr 所指的结构会被系统填入远程主机的地址数据(客户端所传来的服务器端的具体地址), 参数addrlen 为scokaddr 的结构长度.

        再次调用accept()可以接受下一个客户端的连接请求,并再次返回一个新的套接字(与socket()返回的套接字、之前accept()返回的套接字都不同的新的套接字)。这个新的套接字用于与这次接受的客户端之间的通信。

2.connect()函数

 connect()用来将参数sockfd 的socket 连至参数serv_addr 指定的网络地址. 结构sockaddr请参考bind(). 参数addrlen 为sockaddr 的结构长度.

connect函数完成主动连接的过程,功能是完成一个有连接协议的连接过程,对于TCP来说就是那个三路握手过程.

int connect(int sockfd, const struct sockaddr* server_addr,socklen_t addrlen)
返回:0──成功, -1──失败。

参数sockfd

指定数据发送的套接字,解决从哪里发送的问题。内核需要维护大量IO通道,所以用户必需通过这个参数告诉内核从哪个IO通道,此处就是从哪个socket接口中发送数据。sockfd是先前socket返回的值。

参数server_addr

指定数据发送的目的地,也就是服务器端的地址。这里服务器是针对connect说的,因为connect是主动连接的一方调用的,所以相应的要存在一个被连接的一方,被动连接的一方需要调用listen以接受connect的连接请求,如此被动连接的一方就是服务器了。

参数addrlen

指定server_addr结构体的长度。我们知道系统中存在大量的地址结构,但socket接口只是通过一个统一的结构来指定参数类型,所以需要指定一个长度,以使内核在进行参数复制的时候有个有个界限。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值