Socket——进程间(远程)通信的一种机制。
套接字(Socket):IP地址与端口号的组合。
两方进行通信时,必须要知道对方主机的IP地址和进程的端口号。
在 Linux 中的网络编程是通过 socket 接口来进行的。套接字(socket)是一种特殊的 I/O 接口,它也是一种文件描述符。socket 也有一个类似于打开文件的函数调用,该函数返回一个整型的 socket 描述符,随后的连接建立、数据传输等操作都是通过 socket 来实现的。
套接字可分为3种类型:
(1)字节流Socket(Stream Socket):基于TCP,提供可靠的字节流传输;
(2)数据报Socket(Datagram Socket):基于UDP,提供不可靠的报文传输;
(3)原始套接字 Raw Socket:基于IP,允许用户直接对IP操作;
Socket操作的常用系统调用:
- socket():建立Socket端点,获得Socket描述符
- bind():Server绑定Socket地址(IP地址+端口号)
- listen():Server等待Client连接,是一个阻塞操作
- connect():Client连接到Server
- accept():Server获得连接请求的 Client的Socket地址
- send()、recv():在已建立的连接上发送、接收数据(TCP方式)
- sendto()、recvfrom():无需连接,直接发送、接收数据(UDP方式)
- close():关闭Socket(单向/双向)
基于TCP的Socket程序流程:
具体步骤:
- 创建服务端socket,绑定建立连接的端口。
- 服务端程序在一个端口调用监听后,处于阻塞状态,等待客户机的连接。
- 创建客户端socket对象。
- 客户端指定主机名称或IP地址、连接端口号。
- 客户机socket发起连接请求。
- 建立连接。
- 利用send()和recv()进行数据传输。
- 关闭socket。
server.c
#include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <netinet/in.h> int main() { int sockfd,new_fd,numbytes; struct sockaddr_in my_addr; struct sockaddr_in their_addr; int sin_size; char buff[100]; //服务器创建socket套接字描述符 if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) { perror("socket!"); exit(1); } printf("socket Success!,sockfd=%d ",sockfd); //初始化sockaddr结构体,设置绑定4321端口 my_addr.sin_family=AF_INET; my_addr.sin_port=htons(4321);//数据格式转换 my_addr.sin_addr.s_addr=INADDR_ANY;//本机地址 bzero(&(my_addr.sin_zero),8); //绑定套接字描述符sockfd if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))==-1) { perror("bind"); exit(1); } printf("bind Success! "); //创建监听套接字 if(listen(sockfd,10)==-1) { perror("listen"); exit(1); } printf("Listen..... "); //服务器阻塞监听套接字,等待客户端连接 while(1) { sin_size=sizeof(struct sockaddr_in); //如果建立连接,产生新套接字,用于与客户端通信 if((new_fd = accept(sockfd,(struct sockaddr *)&their_addr,&sin_size))==-1) { perror("accept"); exit(1); } //生成一个子进程来进行会话,父进程继续监听 if(!fork()) { //读取客户端发来的信息 if((numbytes = recv(new_fd,buff,60,0))==-1) { perror("recv"); exit(1); } printf("%s ",buff); //发送信息到客户端 if(send(new_fd,"Welcome ,This is server.",60,0)==-1) perror("send"); //本次通信结束 close(new_fd); exit(0); } // } close(sockfd); return 0; }
client.c
#include <stdio.h> #include <errno.h> #include <string.h> #include <stdlib.h> #include <netdb.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> int main(int argc ,char * argv[])//命令行传递地址 { int sockfd,numbytes; char buff[100]; struct hostent * he; struct sockaddr_in their_addr; int i=0; //命令行中的第二个参数为服务器IP he=gethostbyname(argv[1]); //客户端建立socket套接字描述符 if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) { perror("socket!"); exit(1); } //printf("socket Success!,sockfd=%d ",sockfd); their_addr.sin_family=AF_INET; their_addr.sin_port=htons(4321); their_addr.sin_addr=*((struct in_addr *)he->h_addr); bzero(&(their_addr.sin_zero),8); //向服务器发起连接 if(connect(sockfd,(struct sockaddr *)&their_addr,sizeof(struct sockaddr))==-1) { perror("connect"); exit(1); } //向服务器发送字符串 if(send(sockfd,"hello!I am client.",26,0)==-1) { perror("send"); exit(1); } //接收从服务器返回的信息 if((numbytes = recv(sockfd,buff,100,0))==-1) { perror("recv"); exit(1); } printf("recv is :%s ",buff); //通信结束 close(sockfd); return 0; }
创建一个Web程序:
#include<stdio.h>- #include<pthread.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> int KEY_QUIT =0; char referrer[128]; int content_length; #define SERVER_PORT 80 static char copybuf[16384]; //复制一个word文档副本 int copy(FILE * read_f ,FILE * write_f) { int n; int wrote; n=fread(copybuf ,1,sizeof(copybuf),read_f); wrote=fwrite(copybuf,n,1,write_f); return 0; } //发送HTML文件内容 int DoHTML(FILE * f ,char * name) { char * buf; FILE * infile; infile=fopen(name,"r"); copy(infile,f); fclose(infile); return 0; } //解析客户需求 int ParseReq(FILE * f ,char * r) { char * bp; char * c; #ifdef BEBUG printf("req is %s ",r); #endif while(*(++r)!=' '); //判断是否为空白符(空格、换页、回车、制表符等) while(isspace(*r)) r++; while(*r=='/') r++; bp=r; while(*r &&(*(r)!=' ')&&(*(r)!='?')) r++; *r=0; c=bp; DoHTML(f,c); return 0; } //客户连接处理 int HandleConnect(int fd) { FILE *f; char buf[160]; f=fdopen(fd,"a+"); setbuf(f,0); fgets(buf,150,f); #ifdef DEBUG printf("buf=%s ",buf); #endif ParseReq(f,buf); fflush(f); fclose(f); return 1; } //按Q键退出服务程序 void * Key_in(void * data) { int c; for(;;) { c=getchar(); if(c=='q'||c=='Q') { KEY_QUIT=1; exit(10); break; } } } int main(int argc, char * argv[]) { int fd,sockfd; int len; volatile int true=1; struct sockaddr_in ec; struct sockaddr_in server_sockaddr; pthread_t th_key; printf("starting httpd... "); printf("press q to quit "); //建立socket套接字 sockfd =socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); setsockopt(sockfd,SOL_SOCKET, SO_REUSEADDR, (void *)&true ,sizeof(true)); server_sockaddr.sin_family =AF_INET; server_sockaddr.sin_port=htons(SERVER_PORT); server_sockaddr.sin_addr.s_addr=htonl(INADDR_ANY); //把套接字socket与地址、端口号绑在一起 bind(sockfd,(struct sockaddr *)&server_sockaddr, sizeof(server_sockaddr)); //设置监听 listen(sockfd,8*3);
//按Q键退出程序
//pthread_create(&th_key,NULL,Key_in,NULL); //等待客户端连接请求 printf("waitting for connection... "); while(1) { len=sizeof(ec); if((fd=accept(sockfd,(void *)&ec,&len))==-1) { exit(5); close(sockfd); } HandleConnect(fd); } }
然后再写一个网页脚本文件index.html。程序编译运行后,打开浏览器:http://IP地址/index.html。