UNIX系统中的网络协议是这样分层的:1.应用层(telnet,ftp等);2.主机到主机传输层(TCP,UDP);3.Internet层(IP和路由);4.网络访问层(网络,数据链路,物理层)。
套接口描述符使用 sockaddr_in 数据结构,有了套接口之后需要调用bind()函数把套接口绑定到本地计算机的一个接口上,使用inet_addr()函数将普遍形式的IP地址转化为无符号的整型数,调用socket()函数获得文件描述符。程序分为客户端和服务端。应用select函数来实现异步的读写操作。在服务器端:首先先创建套接字,然后绑定,接下进入一个无限循环,用accept函数,接受“连接”请求,然后调用创建线程函数,创造新的线程,进入下一个循环。这样每当有一个新的“连接”被接受都会创建一个新的线程。
服务器端程序如下:
//server.c
1 //server.c 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <errno.h> 5 #include <string.h> 6 #include <sys/types.h> 7 #include <netinet/in.h> 8 #include <sys/socket.h> 9 #include <sys/wait.h> 10 #include <unistd.h> 11 #include <arpa/inet.h> 12 #include <sys/time.h> 13 #include <sys/types.h> 14 15 #define MAXBUF 1024 16 17 18 int main(int argc, char **argv) 19 { 20 int sockfd, new_fd; 21 socklen_t len; 22 struct sockaddr_in my_addr, their_addr; 23 unsigned int myport, lisnum; 24 char buf[MAXBUF + 1]; 25 fd_set rfds; 26 struct timeval tv; 27 int retval, maxfd = -1; 28 29 if (argv[1]) 30 myport = atoi(argv[1]); 31 else 32 myport = 7838; 33 34 if (argv[2]) 35 lisnum = atoi(argv[2]); 36 else 37 lisnum = 2; 38 39 if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) 40 { 41 perror("socket"); 42 exit(1); 43 } 44 45 bzero(&my_addr, sizeof(my_addr)); 46 my_addr.sin_family = PF_INET; 47 my_addr.sin_port = htons(myport); 48 if (argv[3]) 49 my_addr.sin_addr.s_addr = inet_addr(argv[3]); 50 else 51 my_addr.sin_addr.s_addr = INADDR_ANY; 52 53 if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) 54 == -1) { 55 perror("bind"); 56 exit(1); 57 } 58 59 if (listen(sockfd, lisnum) == -1) { 60 perror("listen"); 61 exit(1); 62 } 63 64 while (1) { 65 printf 66 (" ----等待新的连接到来开始新的聊天…… "); 67 len = sizeof(struct sockaddr); 68 if ((new_fd = 69 accept(sockfd, (struct sockaddr *) &their_addr, 70 &len)) == -1) { 71 perror("accept"); 72 exit(errno); 73 } else 74 printf("server: got connection from %s, port %d, socket %d ", 75 inet_ntoa(their_addr.sin_addr), 76 ntohs(their_addr.sin_port), new_fd); 77 78 /* 开始处理每个新连接上的数据收发 */ 79 printf 80 (" 准备就绪,可以开始聊天了……输入消息回车即可发信息给对方 "); 81 while (1) { 82 /* 把集合清空 */ 83 FD_ZERO(&rfds); 84 /* 把标准输入句柄0加入到集合中 */ 85 FD_SET(0, &rfds); 86 maxfd = 0; 87 /* 把当前连接句柄new_fd加入到集合中 */ 88 FD_SET(new_fd, &rfds); 89 if (new_fd > maxfd) 90 maxfd = new_fd; 91 /* 设置最大等待时间 */ 92 tv.tv_sec = 1; 93 tv.tv_usec = 0; 94 /* 开始等待 */ 95 retval = select(maxfd + 1, &rfds, NULL, NULL, &tv); 96 if (retval == -1) { 97 printf("将退出,select出错! %s", strerror(errno)); 98 break; 99 } else if (retval == 0) { 100 /* printf 101 ("没有任何消息到来,用户也没有按键,继续等待…… "); */ 102 continue; 103 } else { 104 if (FD_ISSET(0, &rfds)) { 105 /* 用户按键了,则读取用户输入的内容发送出去 */ 106 bzero(buf, MAXBUF + 1); 107 fgets(buf, MAXBUF, stdin); 108 if (!strncasecmp(buf, "quit", 4)) { 109 printf("自己请求终止聊天! "); 110 break; 111 } 112 len = send(new_fd, buf, strlen(buf) - 1, 0); 113 if (len > 0) 114 printf 115 ("消息:%s 发送成功,共发送了%d个字节! ", 116 buf, len); 117 else { 118 printf 119 ("消息'%s'发送失败!错误代码是%d,错误信息是'%s' ", 120 buf, errno, strerror(errno)); 121 break; 122 } 123 } 124 if (FD_ISSET(new_fd, &rfds)) { 125 /* 当前连接的socket上有消息到来则接收对方发过来的消息并显示 */ 126 bzero(buf, MAXBUF + 1); 127 /* 接收客户端的消息 */ 128 len = recv(new_fd, buf, MAXBUF, 0); 129 if (len > 0) 130 printf 131 ("接收消息成功:'%s',共%d个字节的数据 ", 132 buf, len); 133 else { 134 if (len < 0) 135 printf 136 ("消息接收失败!错误代码是%d,错误信息是'%s' ", 137 errno, strerror(errno)); 138 else 139 printf("对方退出了,聊天终止 "); 140 break; 141 } 142 } 143 } 144 } 145 close(new_fd); 146 /* 处理每个新连接上的数据收发结束 */ 147 printf("还要和其它连接聊天吗?(no->退出)"); 148 fflush(stdout); 149 bzero(buf, MAXBUF + 1); 150 fgets(buf, MAXBUF, stdin); 151 if (!strncasecmp(buf, "no", 2)) { 152 printf("终止聊天! "); 153 break; 154 } 155 } 156 157 close(sockfd); 158 return 0; 159 }
客户端程序如下:
//client.c
1 #include <stdio.h> 2 #include <string.h> 3 #include <errno.h> 4 #include <sys/socket.h> 5 #include <resolv.h> 6 #include <stdlib.h> 7 #include <netinet/in.h> 8 #include <arpa/inet.h> 9 #include <unistd.h> 10 #include <sys/time.h> 11 #include <sys/types.h> 12 13 #define MAXBUF 1024 14 15 int main(int argc, char **argv) 16 { 17 int sockfd, len; 18 struct sockaddr_in dest; 19 char buffer[MAXBUF + 1]; 20 fd_set rfds; 21 struct timeval tv; 22 int retval, maxfd = -1; 23 24 if (argc != 3) { 25 printf 26 ("参数错误!正确用法如下: %s IP地址 端口 比如: %s 127.0.0.1 80 此程序从某个IP地址的服务器某个端口接收最多MAXBUF个字节的消息", 27 argv[0], argv[0]); 28 exit(0); 29 } 30 /* 创建一个 socket 用于 tcp 通信 */ 31 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 32 perror("Socket"); 33 exit(errno); 34 } 35 36 /* 初始化服务器端(对方)的地址和端口信息 */ 37 bzero(&dest, sizeof(dest)); 38 dest.sin_family = AF_INET; 39 dest.sin_port = htons(atoi(argv[2])); 40 if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) { 41 perror(argv[1]); 42 exit(errno); 43 } 44 45 /* 连接服务器 */ 46 if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) { 47 perror("Connect "); 48 exit(errno); 49 } 50 51 printf 52 (" 准备就绪,可以开始聊天了……输入消息回车即可发信息给对方 "); 53 while (1) { 54 /* 把集合清空 */ 55 FD_ZERO(&rfds); 56 /* 把标准输入句柄0加入到集合中 */ 57 FD_SET(0, &rfds); 58 maxfd = 0; 59 /* 把当前连接句柄sockfd加入到集合中 */ 60 FD_SET(sockfd, &rfds); 61 if (sockfd > maxfd) 62 maxfd = sockfd; 63 /* 设置最大等待时间 */ 64 tv.tv_sec = 1; 65 tv.tv_usec = 0; 66 /* 开始等待 */ 67 retval = select(maxfd + 1, &rfds, NULL, NULL, &tv); 68 if (retval == -1) { 69 printf("将退出,select出错! %s", strerror(errno)); 70 break; 71 } else if (retval == 0) { 72 /* printf 73 ("没有任何消息到来,用户也没有按键,继续等待…… "); */ 74 continue; 75 } else { 76 if (FD_ISSET(sockfd, &rfds)) { 77 /* 连接的socket上有消息到来则接收对方发过来的消息并显示 */ 78 bzero(buffer, MAXBUF + 1); 79 /* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */ 80 len = recv(sockfd, buffer, MAXBUF, 0); 81 if (len > 0) 82 printf 83 ("接收消息成功:'%s',共%d个字节的数据 ", 84 buffer, len); 85 else { 86 if (len < 0) 87 printf 88 ("消息接收失败!错误代码是%d,错误信息是'%s' ", 89 errno, strerror(errno)); 90 else 91 printf("对方退出了,聊天终止! "); 92 break; 93 } 94 } 95 if (FD_ISSET(0, &rfds)) { 96 /* 用户按键了,则读取用户输入的内容发送出去 */ 97 bzero(buffer, MAXBUF + 1); 98 fgets(buffer, MAXBUF, stdin); 99 if (!strncasecmp(buffer, "quit", 4)) { 100 printf("自己请求终止聊天! "); 101 break; 102 } 103 /* 发消息给服务器 */ 104 len = send(sockfd, buffer, strlen(buffer) - 1, 0); 105 if (len < 0) { 106 printf 107 ("消息'%s'发送失败!错误代码是%d,错误信息是'%s' ", 108 buffer, errno, strerror(errno)); 109 break; 110 } else 111 printf 112 ("消息:%s 发送成功,共发送了%d个字节! ", 113 buffer, len); 114 } 115 } 116 } 117 /* 关闭连接 */ 118 close(sockfd); 119 return 0; 120 }
编译过程:gcc -Wall server.c -o server
gcc -Wall client.c -o client
运行:在一个终端里运行 ./server 3490 1 模拟服务器端,其中3490指本地接口
在另一个终端运行 ./client 127.0.0.1 3490 模拟客户端,最后一个参数应与服务器定义的接口相同