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

来源:互联网 发布:lua 读取json文件 编辑:程序博客网 时间:2024/05/16 14:13

需要注意的地方:

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>#defineSERVPORT 3336#define MAXDATASIZE 100main(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 socketif (( 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#defineMAX_CONNECTED_NO 10#define MAXDATASIZE 100int 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接口只是通过一个统一的结构来指定参数类型,所以需要指定一个长度,以使内核在进行参数复制的时候有个有个界限。

0 0