TCP socket编程实现
2017-05-24 19:29
316 查看
下面是TCP socket的经典流程图
服务器端依次调用socket()、bind()、listen()之后,开始监听指定的socket地址。客户端调用socket(),然后再调用connect()向服务器发送一个连接请求。服务器监听到这个请求之后,就会调用accept()完成接收请求,并且返回一个新的套接字描述符,可用于向客户端读取或发送数据,用的分别是recv()和send()函数。同样,客户端connect()成功之后,也可以向服务器读取或发送数据。
接下来,客户端和服务器端分别写一个程序,然后在不同的终端中运行试验。
从上面的程序可以看到,客户端创建socket,然后向服务器发出connect,连接成功后,
会发送字符串”getPwd“到服务器,然后等待接收来自服务器的反馈数据。
程序干了几件事:
1. 服务器创建socket,调用bind绑定地址,listen监听客户端
2. 当监听到客户端的连接,accept返回新的套接字描述符client_fd,通过client_fd,用recv()函数可以接收到客户端发送过来的数据”getPwd”
3. 解析数据,调用getBodyOfMsg得到来自客户端的命令为”getPwd”。调用client_acquire_server_info(char *msg)处理命令,这个函数会去查找匹配到cmdFunc[]数组中的{“getPwd”, getPwdFunc},找到客户端发送命令相对应的getPwdFunc()函数并执行这个函数。
4. 把获取到的密码通过send()函数发送到客户端。
这样写的好处是,以后如果客户端指定更多的命令,服务器端只需要在cmdFunc[]添加相对应的命令和函数,使得程序扩展方便。
接下来实验一下程序。打开终端编译程序,然后执行./server
打开另外一个终端,执行./client 127.0.0.1
得到来自服务器端获取的账号test和密码test
这时查看执行server程序的终端,查看下面打印,可以发现接收客户端的命令是getPwd ,并且程序做出相应处理后把数据发送给了客户端
服务器端依次调用socket()、bind()、listen()之后,开始监听指定的socket地址。客户端调用socket(),然后再调用connect()向服务器发送一个连接请求。服务器监听到这个请求之后,就会调用accept()完成接收请求,并且返回一个新的套接字描述符,可用于向客户端读取或发送数据,用的分别是recv()和send()函数。同样,客户端connect()成功之后,也可以向服务器读取或发送数据。
接下来,客户端和服务器端分别写一个程序,然后在不同的终端中运行试验。
/*client.c*/ #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <unistd.h> #include <netdb.h> #include <netinet/in.h> #include <pthread.h> #define PORT 8019 #define BUFFER_SIZE 4096 #define FILE_NAME_MAX 512 int main(int argc,char* argv[]) { int sockfd; //char buff[BUFFER_SIZE]; struct hostent *host; struct sockaddr_in serv_addr; if(argc != 2) { fprintf(stderr,"Usage: ./client Hostname(or ip address) \ne.g. ./client 127.0.0.1 \n"); exit(1); } //地址解析函数 if ((host = gethostbyname(argv[1])) == NULL) { perror("gethostbyname"); exit(1); } //创建socket if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1) { perror("socket"); exit(1); } bzero(&serv_addr,sizeof(serv_addr)); //设置sockaddr_in 结构体中相关参数 serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); //将16位的主机字符顺序转换成网络字符顺序 serv_addr.sin_addr = *((struct in_addr *)host->h_addr); //获取IP地址 bzero(&(serv_addr.sin_zero), 8); //填充0以保持struct sockaddr同样大小 //调用connect函数主动发起对服务器端的连接 if(connect(sockfd,(struct sockaddr *)&serv_addr, sizeof(serv_addr))== -1) { perror("connect"); exit(1); } char buff[BUFFER_SIZE]= "<letter>getPwd</letter>"; //读写缓冲区 int count; count=send(sockfd,buff,100,0); if(count<0) { perror("Send file informantion"); exit(1); } printf("client send OK count = %d\n",count); //接收来自server的数据 char pbuf[BUFFER_SIZE] = {0}; int length=recv(sockfd,pbuf,100,0); printf("data from pon is %s\n",pbuf); close(sockfd); return 0; }
从上面的程序可以看到,客户端创建socket,然后向服务器发出connect,连接成功后,
会发送字符串”getPwd“到服务器,然后等待接收来自服务器的反馈数据。
/*server.c*/ #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <unistd.h> #include <netinet/in.h> /* 如果把IP地址比作一间房子 ,端口就是出入这间房子的门。端口号就是打开门的钥匙。 真正的房子只有几个门,但是一个IP地址的端口 可以有65536个之多! 端口是通过端口号来标记的,端口号只有整数,范围是从0 到65535。 地址中的端口号必须不小于1024,除非该进程具有相应的特权(即超级用户)。 */ #define PORT 8019 #define RCV_BUF_SIZE 0x1000 #define MAX_QUE_CONN_NM 1 char * getPwdFunc() { //获取密码 printf("Began to get the password\n "); printf("Send the parsed msg to client\n "); char * result = "result=ok|id=test|pwd=test"; return result; } char * setPwdFunc() { printf("Began to get the password\n "); printf("Send the parsed msg to client\n "); char * result = "result=ok"; return result; } typedef char *(*func)(); struct processFun { char *name; func process; }; struct processFun cmdFunc[] = { {"setPwd", setPwdFunc}, {"getPwd", getPwdFunc} }; char * client_acquire_server_info(char *msg) { char *pMsgTmp; int i; for(i=0; i< sizeof(cmdFunc)/sizeof(struct processFun); i++) { if(0 == strcmp(msg,cmdFunc[i].name)) { printf("found it \n"); printf("%s: parameter %s \n",__FUNCTION__,msg); pMsgTmp = cmdFunc[i].process(); break; } } return pMsgTmp; } //获取消息主体 char *getBodyOfMsg(char *buf) { char * pStr1 = NULL; char * pStr2 = NULL; pStr1 = strstr(buf,"<letter>"); pStr2 = strstr(buf,"</letter>"); if (pStr2 == NULL || pStr1 == NULL) { return NULL; } *pStr2 = '\0'; return (pStr1 + strlen("<letter>")); } //解析接收到的字符串,然后调用相应的zte接口返回相关数据发送给客户端 char * parseDataFromClient(char *pmsg) { char *pTmp = getBodyOfMsg(pmsg); if (pTmp == NULL) { printf("parse data failed!\n"); return NULL; } printf("the parsed data is %s \n",pTmp); char * acquireDataFromServer = client_acquire_server_info(pTmp); return acquireDataFromServer; } int main(int argc,char* argv[]) { int sockfd; struct sockaddr_in server_sockaddr, client_sockaddr; //建立socket连接 if ((sockfd = socket(AF_INET,SOCK_STREAM,0))== -1) { perror("socket"); exit(1); } printf("Socket id = %d\n",sockfd); //设置sockaddr_in 结构体中相关参数 server_sockaddr.sin_family = AF_INET; server_sockaddr.sin_port = htons(PORT); server_sockaddr.sin_addr.s_addr = INADDR_ANY; bzero(&(server_sockaddr.sin_zero), 8); //setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); //绑定函数bind if (bind(sockfd, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr))== -1) { perror("bind"); exit(1); } printf("Bind success!\n"); //调用listen函数 if (listen(sockfd, MAX_QUE_CONN_NM) == -1) { perror("listen"); exit(1); } printf("Listening....\n"); char *pbuf = NULL; int client_fd,length; int addrlen = sizeof(server_sockaddr); char *ptmp = NULL; pbuf = (char *)malloc(RCV_BUF_SIZE); if(NULL == pbuf) { printf("igdserver:main:malloc failed in callback\n"); } while(1) { if ((client_fd = accept(sockfd, (struct sockaddr *)&client_sockaddr, (socklen_t *)&addrlen)) == -1) { perror("accept"); exit(1); } memset(pbuf, RCV_BUF_SIZE, 0); length=recv(client_fd,pbuf,100,0); printf("string from client is %s ,length = %d\n",pbuf,length); //服务器开始解析从客户端传过来的数据,并做出相应的处理 ptmp = parseDataFromClient(pbuf); //把得到的结果返回给客户端 int count=send(client_fd,ptmp,100,0); if(count<0) { perror("Send file informantion"); exit(1); } printf("client send OK, count = %d,result = %s\n",count,ptmp); } free(pbuf); close(sockfd); close(client_fd); return 0; }
程序干了几件事:
1. 服务器创建socket,调用bind绑定地址,listen监听客户端
2. 当监听到客户端的连接,accept返回新的套接字描述符client_fd,通过client_fd,用recv()函数可以接收到客户端发送过来的数据”getPwd”
3. 解析数据,调用getBodyOfMsg得到来自客户端的命令为”getPwd”。调用client_acquire_server_info(char *msg)处理命令,这个函数会去查找匹配到cmdFunc[]数组中的{“getPwd”, getPwdFunc},找到客户端发送命令相对应的getPwdFunc()函数并执行这个函数。
4. 把获取到的密码通过send()函数发送到客户端。
这样写的好处是,以后如果客户端指定更多的命令,服务器端只需要在cmdFunc[]添加相对应的命令和函数,使得程序扩展方便。
接下来实验一下程序。打开终端编译程序,然后执行./server
//终端A: ubuntu:~/test/aa$ gcc client.c -o client ubuntu:~/test/aa$ gcc server.c -o server ubuntu:~/test/aa$ ls client client.c server server.c ubuntu:~/test/aa$ ./server Socket id = 3 Bind success! Listening....
打开另外一个终端,执行./client 127.0.0.1
得到来自服务器端获取的账号test和密码test
//终端B: ubuntu:~/test/aa$ ./client 127.0.0.1 client send OK count = 100 data from pon is result=ok|id=test|pwd=test
这时查看执行server程序的终端,查看下面打印,可以发现接收客户端的命令是getPwd ,并且程序做出相应处理后把数据发送给了客户端
//终端A: string from client is <letter>getPwd</letter> ,length = 100 the parsed data is getPwd found it client_acquire_server_info: parameter getPwd Began to get the password Send the parsed msg to client client send OK, count = 100,result = result=ok|id=test|pwd=test
相关文章推荐
- socket 编程 TCP 实现简单聊天功能
- Android 基于TCP的 Socket 编程实现(结合 okio)
- JAVA 通过 Socket 实现 TCP 编程
- [置顶] 使用多线程实现多客户端的连接(通过Socket实现TCP编程)
- 基于C#的socket编程的TCP异步实现
- TCP Socket编程 C/C++实现 (Windows Platform SDK)
- iOS 网络编程实践--NSStream实现TCP Socket iPhone客户端
- java网络编程,通过TCP,Socket实现多对一的局域网聊天室
- C#中使用异步Socket编程实现TCP网络服务的CS的通讯构架(一)----基础类库部分
- C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)
- 基于C#的socket编程的TCP同步实现
- TCP Socket编程 C/C++实现 (Windows Platform SDK)
- java网络编程,通过TCP,Socket实现多对一的局域网聊天室
- iOS网络编程实践--NSStream实现TCP Socket iPhone客户端
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分
- iOS网络编程实践–NSStream实现TCP Socket iPhone客户端
- Python中的TCP编程,实现客户端与服务器的聊天(socket)
- [转载]在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(二)----使用方法
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架