最近学习了socket网络编程。socket处于应用层和传输层之间,应用程序通过socket在不同主机、进程之间连接起来,交换处理数据,从而屏蔽了底层的传输协议细节,如TCP协议中的三次握手、四次挥手。通过socket,客户端可以连接到服务器端,向服务器发出请求,相应地,服务器向客户端返回请求结果。socket是Web开发的基础。
本章实现了一个简单的服务器\客户端模型:
服务器创建一个监听描述符,当有客户端连接请求时,返回一个本地连接描述符conn_sock,客户端发来的数据先缓存在这个描述符上,以供服务器读取。
客户端通过服务器IP地址,端口号发出连接请求,连接成功后返回一个本地的套接字client_sock。客户端向client_sock写入需要发送给服务器的数据,并且从此套接字读取服务器发来的结果。
处理请求本次主要时验证socket通信。服务器将客户端发来的英文字符进行大小写转换并返回给客户端。
下面是源代码:
服务器端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include //gethostbyaddr
#define MAXLINE 1024
/*
toggle是一个只能处理一个服务器请求的简单服务器
*/
void toggle(int conn_sock);
int startup(int *port);
void error_die(const char *sc);
int main(int argc, char **argv){
int listen_sock, conn_sock, port, clientlen;
struct sockaddr_in clientaddr;
struct hostent *hp; //客户端名字信息,通过gethostbyaddr函数获取
char *haddrp; //客户端IP信息,在clientaddr中
if(argc != 2){
fprintf(stderr, "input:%s \n" , argv[0]);
exit(1);
}
port=atoi(argv[1]); //string to int
listen_sock = startup(&port);
while(1){
clientlen=sizeof(clientaddr);
conn_sock=accept(listen_sock,(struct sockaddr*)&clientaddr,&clientlen);
hp=gethostbyaddr((const char*)&clientaddr.sin_addr.s_addr,
sizeof(clientaddr.sin_addr.s_addr),AF_INET);
haddrp=inet_ntoa(clientaddr.sin_addr);
printf("Server connected to %s(%s)\n",hp->h_name,haddrp);
toggle(conn_sock);
close(conn_sock);
}
exit(0);
}
void toggle(int conn_sock){
int n;
int i;
char buf[MAXLINE];
while(n=recv(conn_sock,buf,MAXLINE,0)){
printf("toggle server received %d bytes\n",n);
for(i=0;i<n;i++){
if(isupper(buf[i]))
buf[i]=tolower(buf[i]);
else
buf[i]=toupper(buf[i]);
}
send(conn_sock,buf,n,0);
}
}
int startup(int *port)
{
int httpd = 0;
struct sockaddr_in name;
httpd = socket(PF_INET, SOCK_STREAM, 0);
if (httpd == -1)
error_die("socket");
memset(&name, 0, sizeof(name));
name.sin_family = AF_INET;
name.sin_port = htons(*port);
name.sin_addr.s_addr = htonl(INADDR_ANY);//表示本地IP
//绑定socket
if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
error_die("bind");
//如果端口没有设置,提供个随机端口
if (*port == 0)
{
socklen_t namelen = sizeof(name);
if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
error_die("getsockname");
*port = ntohs(name.sin_port);
}
//监听
if (listen(httpd, 5) < 0)
error_die("listen");
return httpd;
}
void error_die(const char *sc)
{
perror(sc);
exit(1);
}
客户端:
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXLINE 1024
//一个客户端程序
int open_client_sock(char *host,int port);
void error_die(const char *sc);
int main(int argc,char **argv){
int client_sock,port,n;
char *host,buf[MAXLINE];
if(argc!=3){
fprintf(stderr,"input:%s \n" ,argv[0]);
exit(1);
}
host=argv[1]; //char * server's name
port=atoi(argv[2]); //int server's port
client_sock=open_client_sock(host,port);
while(fgets(buf,MAXLINE,stdin)!=NULL){
n = send(client_sock,buf,strlen(buf),0);
printf("send %d bytes\n",n);
recv(client_sock,buf,MAXLINE,0);
fputs(buf,stdout);
}
close(client_sock);
exit(0);
}
//传入一个服务器主机名,监听端口,返回已连接描述符
int open_client_sock(char *host,int port){
int http = 0;
struct hostent *hp;
struct sockaddr_in serveraddr;
http = socket(AF_INET, SOCK_STREAM, 0);
if(http == -1)
error_die("socket");
if((hp = gethostbyname(host)) == NULL)//由域名获取IP
error_die("no host");
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);
bcopy((char *)hp->h_addr_list[0], (char *)&serveraddr.sin_addr.s_addr, hp->h_length);
if(connect(http, (struct sockaddr *)&serveraddr, sizeof(serveraddr))<0)
error_die("can't connect");
return http;
}
void error_die(const char *sc)
{
perror(sc);
exit(1);
}