编写了一个最简单的server/client聊天工具练练手:
server.c 的作用是接受client的请求,并与client进行简单的数据通信,整体为一个阻塞式的网络聊天工具。
server.c:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include <pthread.h> const short port = 8080; char *ip = "127.0.0.1"; void *pthread_run(void *arg) { int fd = (int)arg; char buf[1024]; memset(buf, '\0', sizeof(buf)); while (1) { ssize_t sz = read(fd, buf, sizeof(buf) - 1); if (sz < 0) { perror("read"); break; } else if (0 == sz) { printf("read done...\n"); break; } else { buf[sz - 1] = '\0'; printf("client# %s\n", buf); write(fd, buf, strlen(buf)); } } return 0; } int main() { //1.创建套接字 int listen_sock = socket(AF_INET, SOCK_STREAM, 0); if (listen_sock < 0) { perror("socket"); return 1; } //2.绑定 struct sockaddr_in local; local.sin_family = AF_INET; local.sin_port = htons(port); local.sin_addr.s_addr = inet_addr(ip); if (bind(listen_sock, (struct sockaddr *)&local, sizeof(local)) < 0) { perror("bind"); return 2; } //3.监听 if (listen(listen_sock, 5) < 0) { perror("listen"); return 3; } while (1) { //4.获取收到的套接字 struct sockaddr_in peer; //远端 socklen_t len = sizeof(peer); int fd = accept(listen_sock, (struct sockaddr *)&peer, &len); printf("new connect: %s:%d\n", inet_ntoa(peer.sin_addr), peer.sin_port); pthread_t id = pthread_create(&id, NULL, pthread_run, (void *)fd); //创建线程来服务客户 pthread_detach(id); } return 0; }
client.c:
a#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> const short port = 8080; char *ip = "127.0.0.1"; int main() { //1.创建套接字 int fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return 1; } //2.建立链接 struct sockaddr_in remote; remote.sin_family = AF_INET; remote.sin_port = htons(port); remote.sin_addr.s_addr = inet_addr(ip); if (connect(fd, (struct sockaddr *)&remote, sizeof(remote)) < 0) { perror("connect"); return 2; } char buf[1024]; memset(buf, '\0', sizeof(buf)); while (1) { printf("Please Enter# "); fflush(stdout); ssize_t _s = read(0, buf, sizeof(buf) - 1); if (_s > 0) { buf[_s] = '\0'; } write(fd, buf, strlen(buf)); ssize_t sz = read(fd, buf, sizeof(buf) - 1); if (sz < 0) { perror("read"); return 3; } else if (sz == 0) { printf("read done..."); break; } else { buf[sz] = '\0'; printf("server echo# %s\n", buf); } } return 0; }
socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一一个文件描述符,应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket()调用出错则返回-1。对 于IPv4,family参数指定为AF_INET。对于TCP协议,type参数指定为SOCK_STREAM,表示面向流的传输协议。如果是UDP协议,则type参数指定为SOCK_DGRAM,表示面向数据报的传输协 议。protocol参数的介绍从略,指定为0即可。服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端 口号后就可以向服务器发起连接,因此服务器需要调用bind绑定一个固定的网络地址和端口号。bind()成功返回0,失败返回-1。bind()的作用是将参数sockfd和myaddr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听myaddr所描述的地址和端口号。
典型的服务器程序可以同时服务于多个客户端,当有客户端发起连接时,服务器调用的accept()返回并接受这个连接,如果有大量的客户端发起连接而服务器来不及处理,尚未accept 的客户端就处于连接等待状态,listen()声明sockfd处于监听状态,并且最多允许有backlog个客户端处于连接等待状态,如果接收到更多的连接请求就忽略。
三方握手完成后,服务器调用accept()接受连接,如果服务器调用accept()时还没有客户端的连接请 求,就阻塞等待直到有客户端连接上来。
client.c的作用是链接server,并向server发起通信请求。
由于客户端不需要固定的端口号,因此不必调用bind(),客户端的端口号由内核自动分配。注意, 客户端不是不允许调用bind(),只是没有必要调用bind()固定一个端口号,服务器也不是必须调用bind(),但如果服务器不调用bind(),内核会自动给服务器分配监听端口,每次启动服务器时端口号都不一样,客户端要连接服务器就会遇到麻烦。
演示
先编译运行服务器:
查看监听状态:
运行客户端:
给server发送消息,立即接收到了server的回显消息:
![]()
client退出后:
图示:
最简单的server/client程序
最新推荐文章于 2024-07-24 15:57:32 发布