标签:网络编程 服务器客户端 服务器客户端交互 server client
这是一个简单的TCP服务器/客户端的程序示例。客户端发送两个long型变量到服务器端,服务器端读取这两个long型变量并返回这两个变量的和给客户端。
这是服务器端的示例代码:
#include <stdio.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> #include <fcntl.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <sys/types.h> #include <sys/wait.h> #define MAXLINE 1024 #define LISTENQ 5 void str_echo(int connfd); void sig_chld(int sign); void err_sys(char *str); int main(void) { int listenfd, connfd; pid_t childpid; socklen_t clilen; struct sockaddr_in servaddr, cliaddr; if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) err_sys("socket error"); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(4321); //4321端口是自己填写的临时端口,只要和客户端上填写的服务器端口是该临时端口就可以了 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) err_sys("bind error"); if(listen(listenfd, LISTENQ) < 0) err_sys("listen error"); signal(SIGCHLD, sig_chld); for( ; ; ) { clilen = sizeof(cliaddr); if((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen)) < 0) { //if(errno == EINTR) //如果在Linux下的vim编写的话可以把这三行注释到的代码给加上去 // continue; //else err_sys("accept error"); } if((childpid = fork()) == 0) { printf("I am in child %d\n", getpid()); close(listenfd); str_echo(connfd); close(connfd); exit(0); } close(connfd); } return 0; } /* * the function to handle INT of SIGCHLD */ void sig_chld(int sign) { pid_t pid; int stat; while((pid = waitpid(-1, &stat, WNOHANG)) > 0) printf("child %d terminated\n", pid); return; } /* * send the sum of two long numbers to client */ void str_echo(int sockfd) { long arg1, arg2; ssize_t n; char line[MAXLINE]; for( ; ; ) { if((n = read(sockfd, line, MAXLINE)) == 0) return; if(sscanf(line, "%ld%ld", &arg1, &arg2) == 2) snprintf(line, sizeof(line), "%ld\n", arg1 + arg2); else snprintf(line, sizeof(line), "input error"); n = strlen(line); if(write(sockfd, line, n) != n) err_sys("write error"); } } void err_sys(char *str) { perror(str); exit(1); }
#include <stdio.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> #include <fcntl.h> #include <string.h> #include <unistd.h> #define MAXLINE 1024 void str_cli(int sockfd); void err_sys(char *str); int main(int argc, char *argv[]) { int sockfd; struct sockaddr_in servaddr; if(argc != 2) { printf("usage: ./a.out <ip>\n"); exit(1); } if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) err_sys("socket error"); printf("sockfd is ok\n"); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(4321); if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) < 0) err_sys("inet_pton error"); if(connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) err_sys("connect error"); printf("connect is ok\n"); str_cli(sockfd); close(sockfd); return 0; } /* * send two long num to server host */ void str_cli(int sockfd) { char sendline[MAXLINE], recvline[MAXLINE]; while(fgets(sendline, MAXLINE, stdin) != NULL) { if(write(sockfd, sendline, strlen(sendline)) < 0) err_sys("write error"); if(read(sockfd, recvline, MAXLINE) == 0) err_sys("read error"); fputs(recvline, stdout); bzero(recvline, strlen(recvline)); } } void err_sys(char *str) { perror(str); exit(1); }
基本套接字函数讲解
socket函数
#include <sys/socket.h> int socket(int family, int type, int protocol); //成功返回非负描述符,出错返回-1socket函数指定期望的通信协议类型(使用IPv4的TCP、使用IPv6的UDP、Unix域字节流协议)和套接字字类型(字节流、数据报或原始套接字)
----socket函数的family常值 ---------------
family 说明
AF_INET IPv4协议
AF_INET6 IPv4协议
AF_LOCAL Unix协议域
AF_ROUTE 路由套接字
AF_KEY 秘钥套接字
----------------------------------------------------
----socket函数的type常值 ------------------
SOCK_STREAM 字节流套接字
SOCK_DGRAM 数据报套接字
SOCK_SEQPACKET 有序分组套接字
SOCK_RAW 原始套接字
----------------------------------------------------
----socket函数的protocal常值 ------------
IPPROTO_CP TCP传输协议
IPPROTO_UDP UDP传输协议
IPPROTO_SCTP SCTP传输协议
----------------------------------------------------
connect函数
#include <sys/socket.h> int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen); //成功返回0,出错为-1TCP客户用connect函数来建立一个与TCP服务器连接,sockfd是由socket函数返回的套接字描述符,第二个、第三个参数分别是指向一个套接字地址结构的指针和该结构的大小,套接字结构必须含有服务器的IP地址和端口号。如果connect失败后,就必须close当前的套接字描述符并重新调用socket。
#include <sys/socket.h> int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen); //成功返回0,出错-1
bind函数把一个本地协议地址赋予一个套接字,它只是把一个协议地址赋予一个套接字,至于协议地址的含义则取决于协议本身。第二个参数指向协议地址结构的指针,第三个参数是协议地址的长度,对于TCP,调用bind函数可以指定一个端口号,或指定一个IP地址,或两者都指定,也可以两者都不指定。
listen函数
#include <sys/socket.h> int listen(int sockfd, int backlog); //成功返回0,出错-1socket创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的一个客户套接字。listen函数把一个未连接的套接字转换为一个被动套接字,指示内核应接受指向该套接字的连接请求,调用listen函数将导致套接字从CLOSEE状态转换到LISTEN状态。第二个参数规定了内核应为相应套接字排队的最大连接个数。
(2)、已完成连接队列:每个完成TCP三路握手过程的客户对应其中一项,这些套接字处于ESTABLISHED状态。
------------------------------------------------------------------------------------------------------------------------------------------------------
TCP监听套接字维护的两个队列
accept函数
#include <sys/socket.h> int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen); //成功返回非负描述符,出错返回-1如果accept成功,那么其返回值是由内核自动生成的一个全新套接字,代表与返回客户的TCP连接,函数的第一个参数为监听套接字,返回值为已连接套接字。
------------------------------------------------------------------------------------------------------------------------------------------------------
服务器、客户端程序流程图
------------------------------------------------------------------------------------------------------------------------------------------------------
TCP状态转换图
------------------------------------------------------------------------------------------------------------------------------------------------------
参考资料:
1、《UNIX网络编程 卷1 套接字联网API》 第四章-基本套接字编程
2、《计算机网络 - 谢希仁》
标签:网络编程 服务器客户端 服务器客户端交互 server client
原文地址:http://blog.csdn.net/u012796139/article/details/44984879