网络编程之服务器与客户端的建立

来源:互联网 发布:卸载软件工具下载 编辑:程序博客网 时间:2024/06/06 01:26

一什么是 套接字:

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个Socket。Socket的英文原意是“插座”,通常称之为套接字,来描述IP地址和端口,是一个通信链的句柄,用来实现不同虚拟机或者计算机之间的通信。 
在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,与不同客户端的不同服务对应着不同的Socket,这样实现了与多个服务器进行不同服务的功能,因为Socket的不同,这些客户端或服务不会混淆。 
应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字 (Socket)的接口,区分不同应用程序进程间的网络通信和连接。

二 套接字socket类型:

1. 流式套接字(SOCK_STREAM) 
提供面向连接,可靠的数据传输服务,数据无差错,无重复的发送,且按发送顺序接受。 
2. 数据报式套接字(SOCK_DGRAM) 
提供了一个无连接服务。数据包以独立包形式被发送,不提供无错保证,数据可能丢失或重复,并且接受顺序混乱。网络文件系统(NFS)使用数据报式套接字。 
3. 原始套接字(SOCK_RAW) 
该接口允许对较低层协议,如IP,ICMP直接访问。常用于检验新的协议实现或者访问现在服务中配置的新设备。

三 基于TCP的socket编程

tcp中服务器端的流程如下: 
1. 创建套接字(Socket)。 
2. 将套接字绑定到一个本地地址和端口上(bind)。 
3. 将套接字设为监听模式,准备接受客户请求(listen)。 
4. 等待客户请求到来,当请求到来后,接受连接请求。返回一个新的对应于此次连接的套接字(accept)。 
5. 用于返回的套接字和客户端进行通信(send/recv)。 
6. 返回,等待另一客户请求。 
7. 关闭套接字(close)。

四 相关函数说明:

1,创建套接字—socket() 
应用程序在使用套接字前,首先必须拥有一个套接字,系统调用socket()向应用程序提供创建套接字的手段,其调用格式如下:

#include <sys/socket.h>
int socket(int family, int type, int protocol);
该调用要接收三个参数: family、type、protocol。 
参数 family指定通信发生的区域,UNIX系统支持的地址族有:AF_UNIX、AF_INET、AF_NS等,而DOS、WINDOWS中仅支持AF_INET,它是网际网区域。因此,地址族与协议族相同。 
参数type 描述要建立的套接字的类型。 
参数protocol说明该套接字使用的特定协议,如果调用者不希望特别指定使用的协议,则置为0,使用默认的连接模式。 
根据这三个参数建立一个套接字,并将相应的资源分配给它,同时返回一个整型套接字号。

2,绑定套接字—bind() 
bind函数把一个本地协议地址赋予一个套接字,对于网际网协议,协议地址是32位的IPv4地址或者是128位的IPv6地址与16位的TCP或UDP端口号的组合。
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
第一个参数sockfd是函数返回的描述符。 
第二个参数是一个指定特定于协议的地址结构的指针。 
第三个参数是该地址结构的长度。

3,监听函数—listen() 
listen函数仅由TCP服务器调用,它做两件事。 
1. 当socket函数创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的客户套接字。listern函数把一个未连接的套接字转换为一个被动套接字,指示内核应接受指向套接字的连接请求。 
2. listen的第二个函数规定内核应该为相应套接字排队的最大连接数。
#include <sys/socket.h>
int listen(int sockfd, int backlog);
listen函数通常应该在调用socket和bind这两个函数之后,并在调用accept函数之前调用。
,
4,建立连接—accept()和connect() 
accept函数由TCP服务器调用,用于从已完成连接队列对头返回下一个已完成连接。如果已完成连接队列为空,那么进程被投入睡眠。
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
参数cliaddr和addrlen用来返回已连接的对端进程的协议地址。如果accept成功,那么其返回值是由内核自动生成的一个全新描述符,代表与所返回客户的TCP连接。
connect函数是由TCP客户端调用,用来建立与TCP服务器的连接。
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
第二个和第三个参数就是连接服务器的套接字地址结构的指针和该结构的大小,用来将客户端的套接字与服务器套接字相连接,所以套接字地址结构必须含有服务器的IP地址和端口号。 
这两个函数调用用于完成一个完整相关的建立,其中connect()用于建立连接。无连接的 套接字进程也可以调用connect(),但这时在进程之间没有实际的报文交换,调用将从本地操作系统直接返回。这样做的优点是程序员不必为每一数据指定目的地址,而且如果收到的一个数据报,其目的端口未与任何套接字建立“连接”,便能判断该端口不可操作accept() 用于使服务器等待来自某客户进程的实际连接。

5, 关闭套接字—close() 

通常的UNIX close()函数用来关闭套接字,并终止TCP/UDP连接。
#include <unistd.h>
int close(int sockfd);

close一个TCP套接字的默认行为是把该套接字标记为关闭状态,然后立即返回到调用进程。

五 socket的代码实现:

服务器代码:

#include <sys/types.h>#include <sys/socket.h>#include <stdio.h>#include <unistd.h>#include <string.h>#include <arpa/inet.h>#define PORT 9999    //初始化套接字,返回监听套接字int init_socket(){int listen_socket = socket(AF_INET, SOCK_STREAM, 0);if(listen_socket == -1){perror("socket ");return -1;}#if 0//地址结构 struct sockaddr_in    {       short int sin_family;  /* Internet地址族 */       unsigned short int sin_port;  /* 端口号 */       struct in_addr sin_addr;   /* IP地址 */       unsigned char sin_zero[8];  /* 填0 */     };#endif//将套接字绑定到一个本地地址和端口上(bind)struct sockaddr_in addr;memset(&addr, 0, sizeof(&addr));addr.sin_family = AF_INET;    //设置地址族addr.sin_port = htons(PORT);   //设置本地端口addr.sin_addr.s_addr = htonl(INADDR_ANY);   //使用本地任意IP地址//bind 把一个本地协议地址赋予给套接字int ret = bind (listen_socket, (struct sockaddr *)&addr, sizeof(addr));if(ret == -1){perror("bind ");return -1;}//监听本地套接字,准备接受客户端请求ret = listen(listen_socket,5);if(ret == -1){perror("listen ");return -1;}printf("等待客户端连接......\n");return listen_socket;}     //处理客户端连接,返回与连接上的客户信息通信的套接字int MYACCEPT(int listen_socket){/**等待客户请求到来,当请求到来后,接受连接请求。返回一个新的对应于此次连接的套接字(accept)。**///接受连接struct sockaddr_in client_addr;  // 用来保存客户端的IP和端口信息int len = sizeof(client_addr);int client_socket = accept (listen_socket, (struct sockaddr *)&client_addr,&len);if(client_socket == -1){perror("accept ");}/***inet_aton(const char *cp,struct in_addr *inp)表示将a.b.c.d形式的IP转换为32位的IP,存储在 inp指针里面***/printf("成功连接一个客户端 : %s\n",inet_ntoa(client_addr.sin_addr));return client_socket;}//处理客户端连接int Handle_client(int client_socket){char buf[1024];int i;while(1){//从客户端读取数据int ret = read (client_socket, buf, 1024);if(ret == -1){perror("read ");return -1;}//客户端退出if(ret == 0){printf("客户端退出 \n");break;}buf[ret] = '\0';for(i = 0; i < ret - 1; i++){buf[i] = buf[i] + 'A' - 'a';//将小写字母用大写输出}write(client_socket, buf, ret);printf("发送数据:%s\n", buf);}close(client_socket);  //关闭套接字}int main(){int listen_socket = init_socket();  //返回套接字while(1){//获取与客户端连接的套接字int client_socket = MYACCEPT(listen_socket);//处理客户端请求Handle_client(client_socket);}close(listen_socket);//关闭套接字return 0;}


客户端代码:

#include <sys/types.h>#include <sys/socket.h>#include <stdio.h>#include <unistd.h>#include <string.h>#include <arpa/inet.h>#define PORT  9999//客户端向服务器请求服务(进行通信)int Ask(int c_socket){char buf[1024];while(1){fgets(buf,1024,stdin);if(strncmp(buf, "end", 3) == 0){break;}write(c_socket, buf, strlen(buf));int ret = read (c_socket, buf, 1023);if(ret == -1){perror("read ");return -1;}printf(" %s\n",buf);}}int main(){//创建与服务器通信的套接字int c_socket = socket(AF_INET, SOCK_STREAM, 0);if(c_socket == -1){perror("socket ");return -1;}//连接服务器(connect)#if 0//地址结构 struct sockaddr_in    {       short int sin_family;  /* Internet地址族 */       unsigned short int sin_port;  /* 端口号 */       struct in_addr sin_addr;   /* IP地址 */       unsigned char sin_zero[8];  /* 填0 */     };#endif//将套接字绑定到一个本地地址和端口上(bind)struct sockaddr_in addr;memset(&addr, 0, sizeof(&addr));addr.sin_family = AF_INET;    //设置地址族addr.sin_port = htons(PORT);   //设置本地端口//addr.sin_addr.s_addr = htonl(INADDR_ANY);   //使用本地任意IP地址//将a.b.c.d形式的IP转换为32位的IP,存储在 inp指针里面。inet_aton("127.0.0.1",&(addr.sin_addr));//连接服务器成功返回 0 ,失败返回 -1//成功即可通过c_socket 与服务器进行通信int ret = connect (c_socket, (struct sockaddr*)&addr,sizeof(addr));if(ret == -1){perror("connect ");return -1;}printf("成功连接服务器 \n");//和服务器进行通信(请求服务)Ask(c_socket);//关闭套接字close(c_socket);return 0;}








阅读全文
0 0
原创粉丝点击