TCP 服务器客户端程序设计

来源:互联网 发布:软件硬件英语 编辑:程序博客网 时间:2024/05/22 18:09

一、socket 简介

  1、socket 是网络编程的一种接口,它是一种接口,它是一种特殊的I/O,用socket函数建立一个socket 连接,此函数返回一个整形的socket描述符,随后进行数据传输。

    

-----------------------------------------------------------------
 #include <sys/socket.h>
 int socket(int family,int type,int protocol);    
      返回:非负描述字---成功   -1---失败
 -----------------------------------------------------------------

  第一个参数指明了协议簇,目前支持5种协议簇,最常用的有AF_INET(IPv4协议)和AF_INET6(IPv6协议);第二个参数指明套接口类型,有三种类型可选:SOCK_STREAM(字节流套接口)、SOCK_DGRAM(数据报套接口)和SOCK_RAW(原始套接口);如果套接口类型不是原始套接口,那么第三个参数就为0。

       通常socket分为三种类型:流式socket  数据报socket 原始socket

      注意:一个完整的socket有一个本地唯一的socket号,由操作系统分配。最重要的是面向客户/服务器模型而设计的。

  2、一个IP地址,一个通讯端口,就能确定一个通讯程序的位置,为此开发人员专门设计了一个套接结构,就是把网络程序中所用到的网络地址和端口信息放在一个结构体中。

       一般套接口地址结构都以“socketaddr"开头。socket 根据所使用的协议的不同可分为TCP套接口和UDP套接口,又称为流式套接口和数据套接口。

        UDP是一个无连接协议,TCP是个可靠的端对端协议。传输UDP数据包时,LINUX不知道也不关心它们是否已经安全到达目的地,而传输TCP数据包时,则应先建立连接以保证传输的数据被正确接收。

3、两个重要的数据类型:sockadd和sockaddr_in,这两个结构类型都是用来保存socket信息的,如IP地址、通信端口等。

          

         

3、TCP编程 

        这里与  电话通信进行对比

        

      下面是各个函数的功能

     b

bind 函数

    

    完成此步,该socket拥有了本地IP地址,端口,通信协议,不能接收客户端的请求,但可以向服务器发起连接。

第二个参数是一个指向sockaddr结构的指针。structsockaddr只是提供地址类型规范,根据不同的应用,sockaddr需要选用不同的类型。
        


connect函数

   

 其第一个参数为socket返回的文件描述符。
第二个参数储存了服务器端的地址(包括服务器的IP地址和端口信息)。
第三个参数为该地址的长度。
如果执行成功,此函数将与地址为addr的服务器建立连接,并返回0,如果失败则返回-1。
正确完成此步:客户端socket拥有了目的IP,端口信息。

  Listen函数

 


    第一个参数是绑定了IP及端口信息的socket文件描述符。
第二个参数为请求排队的最大长度。当有多个客户端程序和服务器端相连时, 此值表示可以使用的处于等待的队列长度。
listen 函数将绑定的socket文件描述符变为监听套接字,完成此步: 服务器已经准备接收客户端连接请求了。

 accept 函数



第一个参数是监听网络后的socket文件描述符。
第二参数为struct sockaddr 类型的地址空间首地址,第三个参数为该段地址空间长度,因此第二个参数用来存储客户端的IP地址和端口信息,以便为客户端返回数据。
需要注意的是,如果执行成功,此函数将返回一个新的文件描述符以标识该连接,从而使原来的文件描述符可以继续等待新的连接,这样便可以实现多客户端。如果执行失败,将返回-1。

sends recv 函数





第一个参数为发送的目标socket对象;
第二个参数为欲发送的数据位置;
第三个参数为数据的大小;
第四个参数操作flags,支持的值为0或MSG_OOB(发送带外数据)等。对套接字调用write()的行为与将flags设置为0的send()的行为完全相同。
如果执行成功,此函数将返回发送数据的大小,如果失败,将返回-1

  

具体执行过程如下:

首先,服务器端需要做以下准备工作:
(1)调用socket()函数。建立socket对象,指定通信协议。
(2)调用bind()函数。将创建的socket对象与当前主机的某一个IP地和端口绑定。
(3)调用listen()函数。使socket对象处于监听状态,并设置监听队列大小。
客户端需要做以下准备工作:
(1)调用socket()函数。建立socket()对象,指定相同通信协议。
(2)应用程序可以显式的调用bind()函数为其绑定IP地址和端口,当然,也可以将这工作交给TCP/IP协议栈。
接着建立通信连接:
(1)客户端调用connect()函数。向服务器端发出连接请求。
(2)服务端监听到该请求,调用accept()函数接受请求,从而建立连接,并返回一个新的socket文件描述符专门处理该连接。
然后通信双方发送/接收数据:
(1)服务器端调用write()或send()函数发送数据,客户端调用read()或者recv()函数接收数据。反之客户端发送数据,服务器端接收数据。
(2)通信完成后,通信双方都需要调用close()或者shutdown()函数关闭socket对象

 

以下程序在  Fedora 操作系统上运行成功,首先打开终端,输入命令 vim tcpserver.c   ,然后将代码输入,退出 

执行命令gcc –o tcpserver tcpserver.c生成可执行文件tcpserver 

执行命令./ tcpserver

再开一个“终端”,

输入命令 vim tcpclient.c   ,然后将代码输入,退出

gcc–o tcpclient tcpclient.c生成可执行文件tcpclient。

执行命令./ tcpclient 127.0.0.1。

观察两个“终端”出现的结果。

Server  程序:

  

#include <stdio.h>       #include <stdlib.h>       #include <string.h>       #include <unistd.h>       #include <sys/types.h>       #include <sys/socket.h>       #include <netinet/in.h>       #include <arpa/inet.h>            #define  PORT 1234       #define  BACKLOG 1        int main()       {       int  listenfd, connectfd;       struct  sockaddr_in server;       struct  sockaddr_in client;       socklen_t  addrlen;       if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)       {       perror("Creating  socket failed.");       exit(1);       }       int opt =SO_REUSEADDR;       setsockopt(listenfd,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));       bzero(&server,sizeof(server));       server.sin_family=AF_INET;       server.sin_port=htons(PORT);       server.sin_addr.s_addr= htonl (INADDR_ANY);       if(bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -1) {       perror("Binderror.");       exit(1);       }          if(listen(listenfd,BACKLOG)== -1){  /* calls listen() */       perror("listen()error\n");       exit(1);       }       addrlen =sizeof(client);       if((connectfd = accept(listenfd,(struct sockaddr*)&client,&addrlen))==-1) {       perror("accept()error\n");       exit(1);       }       printf("Yougot a connection from cient's ip is %s, prot is %d\n",inet_ntoa(client.sin_addr),htons(client.sin_port));       send(connectfd,"Welcometo my server.\n",22,0);       close(connectfd);       close(listenfd);return 0;       }
client 程序
#include<stdio.h>       #include <stdlib.h>       #include<unistd.h>       #include<string.h>       #include<sys/types.h>       #include<sys/socket.h>       #include<netinet/in.h>       #include<netdb.h>        #define  PORT 1234       #define  MAXDATASIZE 100        int main(int argc, char *argv[])       {       int  sockfd, num;       char  buf[MAXDATASIZE];       struct hostent *he;       struct sockaddr_in server;       if (argc!=2) {       printf("Usage:%s <IP Address>\n",argv[0]);       exit(1);       }       if((he=gethostbyname(argv[1]))==NULL){       printf("gethostbyname()error\n");       exit(1);       }       if((sockfd=socket(AF_INET, SOCK_STREAM, 0))==-1){       printf("socket()error\n");       exit(1);       }       bzero(&server,sizeof(server));       server.sin_family= AF_INET;       server.sin_port = htons(PORT);       server.sin_addr =*((struct in_addr *)he->h_addr);       if(connect(sockfd,(struct sockaddr *)&server,sizeof(server))==-1){       printf("connect()error\n");       exit(1);       }       if((num=recv(sockfd,buf,MAXDATASIZE,0)) == -1){       printf("recv() error\n");       exit(1);       }       buf[num-1]='\0';       printf("Server Message: %s\n",buf);       close(sockfd);return 0;}
                                             
0 0