阅读目录

  •   基本的局域网聊天
  •   客户端服务端双向异步聊天源码
  •   局域网内服务端和有限个客户端聊天源码
  •   完美异步聊天服务端和客户端源码
  •   C++定时器
  •   select异步代码
  •   pthead多线程

服务端:   服务器端先初始化socket,然后与端口绑定,对端口进行监听,调用accept阻塞,等待客户端连接。   socket() -> bind() -> listen() -> accept() 客户端:   客户端先初始化socket,然后与服务端连接,服务端监听成功则连接建立完成   socket() -> connect()

socket的大概过程是这样的:

  服务端先创建一个套接字,端口绑定,对端口进行监听,调用accpet阻塞,等待客户端连接。客户端创建一个套接字,然后通过三次握手完成tcp连接后服务端accpet返回重新建立一个套接字代表返回客户端的tcp连接,(在accpet成功返回前有一个要注意的是server会有两个队列,一个存放完成三次握手的一个是未完成三次握手的,每次accpet会从完成三次握手的队列中取出一个并一直建立TCP连接,此时才能算是真正的连接成功),完成上面的步骤后即可以开始数据的传输了,数据传输结束后再调用close关闭连接

  此外再说一下select函数在server和client双向通信中的重要作用:网络编程的过程中,经常会遇到许多阻塞的函数,网络编程时使用的recv, recvfrom、connect函数都是阻塞的函数,当函数不能成功执行的时候,程序就会一直阻塞在这里,无法执行下面的代码。selcet函数是一个轮循函数,即当循环询问文件节点,可设置超时时间,超时时间到了就跳过代码继续往下执行,就像我们下面的第一个程序一样,如果不注释掉server的send那么如果server不想client发送消息则进程就会停顿在此处等待server发送无法执行下面的代码,无法接受client发送过来的消息,第二个程序就对此进行的改进,在程序中引入了select当超时后就会跳过当前代码,执行下一步不会一直阻塞。(poll和epoll是对select的改进)

TCP编程的服务器端一般步骤是: UDP编程的服务器端一般步骤是:
1、创建一个socket,用函数socket();   2、设置socket属性,用函数setsockopt(); * 可选 3、绑定IP地址、端口等信息到socket上,用函数bind();  4、开启监听,用函数listen();   5、接收客户端上来的连接,用函数accept();6、收发数据,用函数send()和recv(),或者read()和write();   7、关闭网络连接;   8、关闭监听; 1、创建一个socket,用函数socket();  2、设置socket属性,用函数setsockopt();* 可选   3、绑定IP地址、端口等信息到socket上,用函数bind();4、循环接收数据,用函数recvfrom(); 5、关闭网络连接;
TCP编程的客户端一般步骤是: UDP编程的客户端一般步骤是:
1、创建一个socket,用函数socket();   2、设置socket属性,用函数setsockopt();* 可选   3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选   4、设置要连接的对方的IP地址和端口等属性;   5、连接服务器,用函数connect(); 6、收发数据,用函数send()和recv(),或者read()和write();   7、关闭网络连接; 1、创建一个socket,用函数socket();   2、设置socket属性,用函数setsockopt();* 可选   3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选   4、设置对方的IP地址和端口等属性; 5、发送数据,用函数sendto();   6、关闭网络连接;

基本的局域网聊天

局域网TCP服务端:

实现的功能是client到server的半双工通信,server只能接受接收client发送过来的消息,但是不能向client发送消息。

#include <sys/types.h>``#include <sys/socket.h>``#include <stdio.h>``#include <netinet/in.h>``#include <arpa/inet.h>``#include <unistd.h>``#include <string.h>``#include <stdlib.h>``#include <fcntl.h>``#include <sys/shm.h>``#include <thread>``#include <iostream>``#define PORT 7000``#define QUEUE 20//连接请求队列``int` `conn;``void` `thread_task()``{``}` `int` `main()``{``  ``//printf("%d\n",AF_INET);//IPv4协议``  ``printf``(``"%d\n"``,SOCK_STREAM);``//字节流套接字``  ``int` `ss = socket(AF_INET, SOCK_STREAM, 0);``//若成功则返回一个sockfd(套接字描述符)``  ``//printf("%d\n",ss);``  ``struct` `sockaddr_in server_sockaddr;``//一般是储存地址和端口的。用于信息的显示及存储使用``  ``/*设置 sockaddr_in 结构体中相关参数*/``  ``server_sockaddr.sin_family = AF_INET;``  ``server_sockaddr.sin_port = htons(PORT);``//将一个无符号短整型数值转换为网络字节序,即大端模式(big-endian) ``  ``//printf("%d\n",INADDR_ANY);``  ``//INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”。``  ``//一般来说,在各个系统中均定义成为0值。``  ``server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);``//将主机的无符号长整形数转换成网络字节顺序。 ``  ``if``(bind(ss, (``struct` `sockaddr* ) &server_sockaddr, ``sizeof``(server_sockaddr))==-1)``  ``{``    ``perror``(``"bind"``);``    ``exit``(1);``  ``}``  ``if``(listen(ss, QUEUE) == -1)``  ``{``    ``perror``(``"listen"``);``    ``exit``(1);``  ``}` `  ``struct` `sockaddr_in client_addr;``  ``socklen_t length = ``sizeof``(client_addr);``  ``///成功返回非负描述字,出错返回-1``  ``conn = accept(ss, (``struct` `sockaddr*)&client_addr, &length);``  ``//如果accpet成功,那么其返回值是由内核自动生成的一个全新描述符,代表与所返回客户的TCP连接。``  ``//accpet之后就会用新的套接字conn``  ``if``( conn < 0 )``  ``{``    ``perror``(``"connect"``);``    ``exit``(1);``  ``}` `  ``char` `buffer[1024];``  ``//创建另外一个线程``  ``//std::thread t(thread_task);``  ``//t.join();``  ``//char buf[1024];``  ``//主线程``  ``while``(1)``  ``{``  ``//这里把send注释掉了,所以这个程序中server只能是接收client端的数据并能给client发送数据,即使不注释掉也没用,因为没有对是否有数据传入和传入``     ``//进行判断所以按照下面的代码这样写,每次都要先让server输入后才能输出client传过来的数据,若是server不输入则程序无法向下走就没有client发送过来的输出,``     ``//而且每次显示也只能是一行,这样显示就全是错的了,所以就需要select和FD_ISSET的判断了``    ``// memset(buf, 0 ,sizeof(buf));``    ``// if(fgets(buf, sizeof(buf),stdin) != NULL) {``    ``//   send(conn, buf, sizeof(buf), 0);  ``    ``// }` `    ``memset``(buffer, 0 ,``sizeof``(buffer));``    ``int` `len = recv(conn, buffer, ``sizeof``(buffer), 0);``//从TCP连接的另一端接收数据。``    ``/*该函数的第一个参数指定接收端套接字描述符;``    ``第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;``    ``第三个参数指明buf的长度;``    ``第四个参数一般置0*/``    ``if``(``strcmp``(buffer, ``"exit\n"``) == 0)``//如果没有收到TCP另一端发来的数据则跳出循环不输出``    ``{``      ``break``;``    ``}``    ``printf``(``"%s"``, buffer);``//如果有收到数据则输出数据``    ``//必须要有返回数据, 这样才算一个完整的请求``    ``send(conn, buffer, len , 0);``//向TCP连接的另一端发送数据。``  ``}``  ``close(conn);``//因为accpet函数连接成功后还会生成一个新的套接字描述符,结束后也需要关闭``  ``close(ss);``//关闭socket套接字描述符``  ``return` `0;``}

  

  

局域网TCP客户端:

/*局域网TCP客户端*/``#include <sys/types.h>``#include <sys/socket.h>``#include <stdio.h>``#include <netinet/in.h>``#include <arpa/inet.h>``#include <unistd.h>``#include <string.h>``#include <stdlib.h>``#include <fcntl.h>``#include <sys/shm.h>` `#define MYPORT 7000``#define BUFFER_SIZE 1024` `int` `main()``{``  ``///定义sockfd``  ``int` `sock_cli = socket(AF_INET,SOCK_STREAM, 0);` `  ``///定义sockaddr_in``  ``struct` `sockaddr_in servaddr;``  ``memset``(&servaddr, 0, ``sizeof``(servaddr));``  ``servaddr.sin_family = AF_INET;``  ``servaddr.sin_port = htons(MYPORT); ``//服务器端口``  ``servaddr.sin_addr.s_addr = inet_addr(``"127.0.0.1"``); ``//服务器ip,inet_addr用于IPv4的IP转换(十进制转换为二进制)``  ``//127.0.0.1是本地预留地址``  ``//连接服务器,成功返回0,错误返回-1``  ``if` `(connect(sock_cli, (``struct` `sockaddr *)&servaddr, ``sizeof``(servaddr)) < 0)``  ``{``    ``perror``(``"connect"``);``    ``exit``(1);``  ``}` `  ``char` `sendbuf[BUFFER_SIZE];``  ``char` `recvbuf[BUFFER_SIZE];` `  ``while` `(``fgets``(sendbuf, ``sizeof``(sendbuf), stdin) != NULL)``  ``{``/*每次读取一行,读取的数据保存在buf指向的字符数组中,成功,则返回第一个参数buf;*/``    ``send(sock_cli, sendbuf, ``strlen``(sendbuf),0); ``///发送``    ``if``(``strcmp``(sendbuf,``"exit\n"``)==0)``      ``break``;``    ``recv(sock_cli, recvbuf, ``sizeof``(recvbuf),0); ``///接收``    ``fputs``(recvbuf, stdout);` `    ``memset``(sendbuf, 0, ``sizeof``(sendbuf));``//接受或者发送完毕后把数组中的数据全部清空(置0)``    ``memset``(recvbuf, 0, ``sizeof``(recvbuf));``  ``}``  ``close(sock_cli);``  ``return` `0;``}``/*在TCP三次握手完成后会进入等待连接队列,等待服务端调用accpet与之建立连接,这时候是server端调用accept跟客户端建立``通信,客户端并不需要调用accpet,因为有很多个客户端要跟服务端建立连接,这时候服务端就会有一个队列,对已经经过三次握``手的才可以建立连接(类似缓存信息),这个是由服务端来确认的,客户端并不知道什么时候服务端才能跟它建立连接,在服务端``没有调用accept与之连接或者还未排队到它,只能是一直等待,直到服务端准备好了才能跟客户端建立连接,所以主动权在服务端*/

  

客户端服务端双向异步聊天源码

以上的局域网聊天应用有一个很重要的缺点, 服务器只能显示客户端发送的消息, 却无法给客户端发送消息, 这个很尴尬;

通过使用C中的select()函数, 实现一个异步聊天工具:

异步聊天服务端代码:

#include <sys/types.h>``#include <sys/socket.h>``#include <stdio.h>``#include <netinet/in.h>``#include <arpa/inet.h>``#include <unistd.h>``#include <string.h>``#include <stdlib.h>``#include <fcntl.h>``#include <sys/shm.h>``#include <iostream>``#define PORT 7000``#define QUEUE 20` `int` `main()``{``  ``fd_set rfds;``  ``struct` `timeval tv;``  ``int` `retval, maxfd;``  ``int` `ss = socket(AF_INET, SOCK_STREAM, 0);``  ``struct` `sockaddr_in server_sockaddr;``  ``server_sockaddr.sin_family = AF_INET;``  ``server_sockaddr.sin_port = htons(PORT);``  ``//printf("%d\n",INADDR_ANY);``  ``server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);``  ``if``(bind(ss, (``struct` `sockaddr* ) &server_sockaddr, ``sizeof``(server_sockaddr))==-1)``  ``{``    ``perror``(``"bind"``);``    ``exit``(1);``  ``}``  ``if``(listen(ss, QUEUE) == -1)``  ``{``    ``perror``(``"listen"``);``    ``exit``(1);``  ``}` `  ``struct` `sockaddr_in client_addr;``  ``socklen_t length = ``sizeof``(client_addr);``  ``///成功返回非负描述字,出错返回-1``  ``int` `conn = accept(ss, (``struct` `sockaddr*)&client_addr, &length);``  ``/*没有用来存储accpet返回的套接字的数组,所以只能实现server和单个client双向通信*/``  ``if``( conn < 0 )``  ``{``    ``perror``(``"connect"``);``    ``exit``(1);``  ``}``  ``while``(1)``  ``{``    ``/*把可读文件描述符的集合清空*/``    ``FD_ZERO(&rfds);``    ``/*把标准输入的文件描述符加入到集合中*/``    ``FD_SET(0, &rfds);``    ``maxfd = 0;``    ``/*把当前连接的文件描述符加入到集合中*/``    ``FD_SET(conn, &rfds);``    ``/*找出文件描述符集合中最大的文件描述符*/`  `    ``if``(maxfd < conn)``      ``maxfd = conn;``    ``/*设置超时时间*/``    ``tv.tv_sec = 5;``//设置倒计时``    ``tv.tv_usec = 0;``    ``/*等待聊天*/``    ``retval = select(maxfd+1, &rfds, NULL, NULL, &tv);``    ``if``(retval == -1)``    ``{``      ``printf``(``"select出错,客户端程序退出\n"``);``      ``break``;``    ``}``    ``else` `if``(retval == 0)``    ``{``      ``printf``(``"服务端没有任何输入信息,并且客户端也没有信息到来,waiting...\n"``);``      ``continue``;``    ``}``    ``else``    ``{``      ``/*客户端发来了消息*/``      ``if``(FD_ISSET(conn,&rfds))``      ``{``        ``char` `buffer[1024];  ``        ``memset``(buffer, 0 ,``sizeof``(buffer));``        ``int` `len = recv(conn, buffer, ``sizeof``(buffer), 0);``        ``if``(``strcmp``(buffer, ``"exit\n"``) == 0) ``break``;``        ``printf``(``"%s"``, buffer);``        ``//send(conn, buffer, len , 0);把数据回发给客户端``      ``}``      ``/*用户输入信息了,开始处理信息并发送*/``      ``if``(FD_ISSET(0, &rfds))``      ``{``        ``char` `buf[1024];``        ``fgets``(buf, ``sizeof``(buf), stdin);``        ``//printf("you are send %s", buf);``        ``send(conn, buf, ``sizeof``(buf), 0);  ``      ``}``    ``}``  ``}``  ``close(conn);``  ``close(ss);``  ``return` `0;``}

异步聊天客户端代码:

#include <sys/types.h>``#include <sys/socket.h>``#include <stdio.h>``#include <netinet/in.h>``#include <arpa/inet.h>``#include <unistd.h>``#include <string.h>``#include <stdlib.h>``#include <fcntl.h>``#include <sys/shm.h>` `#define MYPORT 7000``#define BUFFER_SIZE 1024``int` `main()``{``  ``int` `sock_cli;``  ``fd_set rfds;``  ``struct` `timeval tv;``  ``int` `retval, maxfd;` `  ``///定义sockfd``  ``sock_cli = socket(AF_INET,SOCK_STREAM, 0);``  ``///定义sockaddr_in``  ``struct` `sockaddr_in servaddr;``  ``memset``(&servaddr, 0, ``sizeof``(servaddr));``  ``servaddr.sin_family = AF_INET;``  ``servaddr.sin_port = htons(MYPORT); ``///服务器端口``  ``servaddr.sin_addr.s_addr = inet_addr(``"127.0.0.1"``); ``///服务器ip` `  ``//连接服务器,成功返回0,错误返回-1``  ``if` `(connect(sock_cli, (``struct` `sockaddr *)&servaddr, ``sizeof``(servaddr)) < 0)``  ``{``    ``perror``(``"connect"``);``    ``exit``(1);``  ``}` `  ``while``(1){``    ``/*把可读文件描述符的集合清空*/``    ``FD_ZERO(&rfds);``    ``/*把标准输入的文件描述符加入到集合中*/``    ``FD_SET(0, &rfds);``    ``maxfd = 0;``    ``/*把当前连接的文件描述符加入到集合中*/``    ``FD_SET(sock_cli, &rfds);``    ``/*找出文件描述符集合中最大的文件描述符*/`  `    ``if``(maxfd < sock_cli)``      ``maxfd = sock_cli;``    ``/*设置超时时间*/``    ``tv.tv_sec = 5;``    ``tv.tv_usec = 0;``    ``/*等待聊天*/``    ``retval = select(maxfd+1, &rfds, NULL, NULL, &tv);``    ``if``(retval == -1)``    ``{``      ``printf``(``"select出错,客户端程序退出\n"``);``      ``break``;``    ``}``    ``else` `if``(retval == 0)``    ``{``      ``printf``(``"客户端没有任何输入信息,并且服务器也没有信息到来,waiting...\n"``);``      ``continue``;``    ``}``    ``else``    ``{``      ``/*服务器发来了消息*/``      ``if``(FD_ISSET(sock_cli,&rfds))``      ``{``        ``char` `recvbuf[BUFFER_SIZE];``        ``int` `len;``        ``len = recv(sock_cli, recvbuf, ``sizeof``(recvbuf),0);``        ``printf``(``"%s"``, recvbuf);``        ``memset``(recvbuf, 0, ``sizeof``(recvbuf));``      ``}``      ``/*用户输入信息了,开始处理信息并发送*/``      ``if``(FD_ISSET(0, &rfds))``      ``{``        ``char` `sendbuf[BUFFER_SIZE];``        ``fgets``(sendbuf, ``sizeof``(sendbuf), stdin);``        ``send(sock_cli, sendbuf, ``strlen``(sendbuf),0); ``//发送``        ``memset``(sendbuf, 0, ``sizeof``(sendbuf));``      ``}``    ``}``  ``}` `  ``close(sock_cli);``  ``return` `0;``}

  

局域网内服务端和有限个客户端聊天源码

以上的局域网聊天只能支持一个用户, 我们还要改改, 必须是支持多用户的聊天室:

局域网TCP2人聊天服务端代码

#include <sys/types.h>``#include <sys/socket.h>``#include <stdio.h>``#include <netinet/in.h>``#include <arpa/inet.h>``#include <unistd.h>``#include <string.h>``#include <stdlib.h>``#include <fcntl.h>``#include <sys/shm.h>``#include <iostream>``#include <thread>``#define PORT 7000``#define QUEUE 20``int` `ss;``struct` `sockaddr_in client_addr;``socklen_t length = ``sizeof``(client_addr);``int` `conns[2] = {};``//定义了一个容量为2的数组来存放套接字,所以server最多只能跟2个client通信``int` `z = 0;``void` `thread_fn()``{``  ``//成功返回非负描述字,出错返回-1``  ``int` `conn = accept(ss, (``struct` `sockaddr*)&client_addr, &length);``  ``if``( conn < 0 )``  ``{``    ``perror``(``"connect"``);``    ``exit``(1);``  ``}``  ``//把连接保存到临时数组中;``  ``conns[z] = conn;``  ``z++;` `  ``fd_set rfds;``  ``struct` `timeval tv;``//linux编程中,如果用到计时,可以用struct timeval获取系统时间``  ``int` `retval, maxfd;``  ``while``(1)``  ``{``    ``/*把可读文件描述符的集合清空*/``    ``FD_ZERO(&rfds);``    ``/*把标准输入的文件描述符加入到集合中*/``    ``FD_SET(0, &rfds);``    ``maxfd = 0;``    ``/*把当前连接的文件描述符加入到集合中*/``    ``FD_SET(conn, &rfds);``    ``/*找出文件描述符集合中最大的文件描述符*/`  `    ``if``(maxfd < conn)``    ``{``      ``maxfd = conn;``    ``}``    ``/*设置超时时间*/``    ``tv.tv_sec = 5;``//5秒``    ``tv.tv_usec = 0;``    ``/*等待聊天*/``    ``retval = select(maxfd+1, &rfds, NULL, NULL, &tv);``    ``if``(retval == -1)``    ``{``      ``printf``(``"select出错,客户端程序退出\n"``);``      ``break``;``    ``}``    ``else` `if``(retval == 0)``    ``{``      ``printf``(``"服务端没有任何输入信息,并且客户端也没有信息到来,waiting...\n"``);``      ``continue``;``    ``}``    ``else``    ``{``      ``/*客户端发来了消息*/``      ``if``(FD_ISSET(conn,&rfds))``//判断conn是否在rfds中如果在返回非零,不再返回0``      ``{``        ``char` `buffer[1024];  ``        ``memset``(buffer, 0 ,``sizeof``(buffer));``//把buffer中的所有值赋值为0,即清空buffer``        ``int` `len = recv(conn, buffer, ``sizeof``(buffer), 0);``//把接收到的数据存放于buffer中``        ``if``(``strcmp``(buffer, ``"exit\n"``) == 0)``//如果接受到的是空的,即没有收到任何信息``          ``break``;``        ``printf``(``"%s"``, buffer);``        ``//send(conn, buffer, len , 0);把数据回发给客户端``      ``}``      ``/*用户输入信息了,开始处理信息并发送*/``      ``if``(FD_ISSET(0, &rfds))``      ``{``        ``char` `buf[1024];``        ``fgets``(buf, ``sizeof``(buf), stdin);``//每次读取一行数据存放在buf中``        ``//printf("you are send %s", buf);``        ``for``(``int` `i=0; i<z; i++)``        ``{``          ``send(conns[i], buf, ``sizeof``(buf), 0);``        ``}  ``      ``}``    ``}``  ``}``  ``close(conn);``}``void` `thread_select(``int` `conn)``{``  ` `}``int` `main()``{``  ``ss = socket(AF_INET, SOCK_STREAM, 0);``//SOCK_STREAM即tcp协议,AF_INET是IPv4套接字``  ``struct` `sockaddr_in server_sockaddr;``  ``server_sockaddr.sin_family = AF_INET;``  ``server_sockaddr.sin_port = htons(PORT);``  ``//printf("%d\n",INADDR_ANY);``  ``server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);``  ``if``(bind(ss, (``struct` `sockaddr* ) &server_sockaddr, ``sizeof``(server_sockaddr))==-1)``  ``{``    ``perror``(``"bind"``);``    ``exit``(1);``  ``}``  ``if``(listen(ss, QUEUE) == -1)``  ``{``    ``perror``(``"listen"``);``    ``exit``(1);``  ``}``  ``std::``thread` `t(thread_fn);``//因为创建了两个线程所以只能连接两个client``  ``std::``thread` `t1(thread_fn);``//这里把收发数据都存放在thread_fn中,所以创建一个这样的线程就能使得server能多连接一个server``  ``t.join();``  ``t1.join();``  ``close(ss);``  ``return` `0;``}

局域网TCP2人聊天客户端代码:

#include <sys/types.h>``#include <sys/socket.h>``#include <stdio.h>``#include <netinet/in.h>``#include <arpa/inet.h>``#include <unistd.h>``#include <string.h>``#include <stdlib.h>``#include <fcntl.h>``#include <sys/shm.h>` `#define MYPORT 7000``#define BUFFER_SIZE 1024``int` `main()``{``  ``int` `sock_cli;``  ``fd_set rfds;``  ``struct` `timeval tv;``  ``int` `retval, maxfd;` `  ``///定义sockfd``  ``sock_cli = socket(AF_INET,SOCK_STREAM, 0);``  ``///定义sockaddr_in``  ``struct` `sockaddr_in servaddr;``  ``memset``(&servaddr, 0, ``sizeof``(servaddr));``  ``servaddr.sin_family = AF_INET;``  ``servaddr.sin_port = htons(MYPORT); ``///服务器端口``  ``servaddr.sin_addr.s_addr = inet_addr(``"127.0.0.1"``); ``///服务器ip` `  ``//连接服务器,成功返回0,错误返回-1``  ``if` `(connect(sock_cli, (``struct` `sockaddr *)&servaddr, ``sizeof``(servaddr)) < 0)``  ``{``    ``perror``(``"connect"``);``    ``exit``(1);``  ``}` `  ``while``(1){``    ``/*把可读文件描述符的集合清空*/``    ``FD_ZERO(&rfds);``    ``/*把标准输入的文件描述符加入到集合中*/``    ``FD_SET(0, &rfds);``    ``maxfd = 0;``    ``/*把当前连接的文件描述符加入到集合中*/``    ``FD_SET(sock_cli, &rfds);``    ``/*找出文件描述符集合中最大的文件描述符*/`  `    ``if``(maxfd < sock_cli)``      ``maxfd = sock_cli;``    ``/*设置超时时间*/``    ``tv.tv_sec = 5;``    ``tv.tv_usec = 0;``    ``/*等待聊天*/``    ``retval = select(maxfd+1, &rfds, NULL, NULL, &tv);``    ``if``(retval == -1)``    ``{``      ``printf``(``"select出错,客户端程序退出\n"``);``      ``break``;``    ``}``else` `if``(retval == 0)``    ``{``      ``printf``(``"客户端没有任何输入信息,并且服务器也没有信息到来,waiting...\n"``);``      ``continue``;``    ``}``    ``else``    ``{``      ``/*服务器发来了消息*/``      ``if``(FD_ISSET(sock_cli,&rfds))``      ``{``        ``char` `recvbuf[BUFFER_SIZE];``        ``int` `len;``        ``len = recv(sock_cli, recvbuf, ``sizeof``(recvbuf),0);``        ``printf``(``"%s"``, recvbuf);``        ``memset``(recvbuf, 0, ``sizeof``(recvbuf));``      ``}``      ``/*用户输入信息了,开始处理信息并发送*/``      ``if``(FD_ISSET(0, &rfds))``      ``{``        ``char` `sendbuf[BUFFER_SIZE];``        ``fgets``(sendbuf, ``sizeof``(sendbuf), stdin);``        ``send(sock_cli, sendbuf, ``strlen``(sendbuf),0); ``//发送``        ``memset``(sendbuf, 0, ``sizeof``(sendbuf));``      ``}``    ``}``  ``}` `  ``close(sock_cli);``  ``return` `0;``}

  

完美异步聊天服务端和客户端源码

以上的多客户聊天不是很好, 因为只允许两个客户端连接, 体验非常差, 如果支持无限个客户端聊天的话那该多好啊, 哈哈, 这个也是可以的, 我们只要使用c++的list即可, 它是可以自增的数组(其实算是链表), 引用 头文件即可:

无限个客户聊天的 服务端代码:

#include <sys/types.h>``#include <sys/socket.h>``#include <stdio.h>``#include <netinet/in.h>``#include <arpa/inet.h>``#include <unistd.h>``#include <string.h>``#include <stdlib.h>``#include <fcntl.h>``#include <sys/shm.h>``#include <iostream>``#include <thread>``#include <list>` `#define PORT 7000``#define IP "127.0.0.1"` `int` `s;``struct` `sockaddr_in servaddr;``socklen_t len;``std::list<``int``> li;``//用list来存放套接字,没有限制套接字的容量就可以实现一个server跟若干个client通信` `void` `getConn()``{``  ``while``(1)``  ``{``    ``int` `conn = accept(s, (``struct` `sockaddr*)&servaddr, &len);``    ``li.push_back(conn);``    ``printf``(``"%d\n"``, conn);``  ``}``}` `void` `getData()``{``  ``struct` `timeval tv;``  ``tv.tv_sec = 10;``//设置倒计时时间``  ``tv.tv_usec = 0;``  ``while``(1)``  ``{``    ``std::list<``int``>::iterator it;``    ``for``(it=li.begin(); it!=li.end(); ++it)``    ``{      ``      ``fd_set rfds;  ``      ``FD_ZERO(&rfds);``      ``int` `maxfd = 0;``      ``int` `retval = 0;``      ``FD_SET(*it, &rfds);``      ``if``(maxfd < *it)``      ``{``        ``maxfd = *it;``      ``}``      ``retval = select(maxfd+1, &rfds, NULL, NULL, &tv);``      ``if``(retval == -1)``      ``{``        ``printf``(``"select error\n"``);``      ``}``      ``else` `if``(retval == 0)``      ``{``        ``//printf("not message\n");``      ``}``      ``else``      ``{``        ``char` `buf[1024];``        ``memset``(buf, 0 ,``sizeof``(buf));``        ``int` `len = recv(*it, buf, ``sizeof``(buf), 0);``        ``printf``(``"%s"``, buf);``      ``}``    ``}``    ``sleep(1);` `  ``}``}` `void` `sendMess()``{``  ``while``(1)``  ``{``    ``char` `buf[1024];``    ``fgets``(buf, ``sizeof``(buf), stdin);``    ``//printf("you are send %s", buf);``    ``std::list<``int``>::iterator it;``    ``for``(it=li.begin(); it!=li.end(); ++it)``    ``{``      ``send(*it, buf, ``sizeof``(buf), 0);``    ``}``  ``}``}` `int` `main()``{``  ``//new socket``  ``s = socket(AF_INET, SOCK_STREAM, 0);``  ``memset``(&servaddr, 0, ``sizeof``(servaddr));``  ``servaddr.sin_family = AF_INET;``  ``servaddr.sin_port = htons(PORT);``  ``servaddr.sin_addr.s_addr = inet_addr(IP);``  ``if``(bind(s, (``struct` `sockaddr* ) &servaddr, ``sizeof``(servaddr))==-1)``  ``{``    ``perror``(``"bind"``);``    ``exit``(1);``  ``}``  ``if``(listen(s, 20) == -1)``  ``{``    ``perror``(``"listen"``);``    ``exit``(1);``  ``}``  ``len = ``sizeof``(servaddr);` `  ``//thread : while ==>> accpet``  ``std::``thread` `t(getConn);``  ``t.detach();``//detach的话后面的线程不同等前面的进程完成后才能进行,如果这里改为join则前面的线程无法判断结束,就会``  ``//一直等待,导致后面的线程无法进行就无法实现操作``  ``//printf("done\n");``  ``//thread : input ==>> send``  ``std::``thread` `t1(sendMess);``  ``t1.detach();``  ``//thread : recv ==>> show``  ``std::``thread` `t2(getData);``  ``t2.detach();``  ``while``(1)``//做一个死循环使得主线程不会提前退出``  ``{` `  ``}``  ``return` `0;``}``/*这个跟前面的不一样的地方是,把获得连接套接字getConn和发送信息sendMess和接收信息getData放在三个函数中,创建``的三个线程分别对应处理三个函数,就可以使得server能跟若干个client通信*/

问:为什么要创建三个线程去处理三个函数,单个线程并不可以吗,多线程和单线程处理起来有什么不同?

答:首先,这里用到多线程的目的是为了提高处理能力,减少等待时间,多线程可以并发执行,即可以同时对三个函数进行处理,处理起来会快很多。这里也是可以用单线程来处理的,但是单线程每次只能做一件事情,不能同时去获得连接套接字、发送消息、接收消息,这样在做其中一件事情的时候其他的两件事情就要等待,这样处理时间会比多线程慢很多。多线程可以及时的响应,单线程不能及时响应。

无限个客户端连接的客户端代码:

#include <sys/types.h>``#include <sys/socket.h>``#include <stdio.h>``#include <netinet/in.h>``#include <arpa/inet.h>``#include <unistd.h>``#include <string.h>``#include <stdlib.h>``#include <fcntl.h>``#include <sys/shm.h>` `#define MYPORT 7000``#define BUFFER_SIZE 1024``int` `main()``{``  ``int` `sock_cli;``  ``fd_set rfds;``  ``struct` `timeval tv;``  ``int` `retval, maxfd;` `  ``///定义sockfd``  ``sock_cli = socket(AF_INET,SOCK_STREAM, 0);``  ``///定义sockaddr_in``  ``struct` `sockaddr_in servaddr;``  ``memset``(&servaddr, 0, ``sizeof``(servaddr));``  ``servaddr.sin_family = AF_INET;``  ``servaddr.sin_port = htons(MYPORT); ``///服务器端口``  ``servaddr.sin_addr.s_addr = inet_addr(``"127.0.0.1"``); ``///服务器ip` `  ``//连接服务器,成功返回0,错误返回-1``  ``if` `(connect(sock_cli, (``struct` `sockaddr *)&servaddr, ``sizeof``(servaddr)) < 0)``  ``{``    ``perror``(``"connect"``);``    ``exit``(1);``  ``}` `  ``while``(1){``    ``/*把可读文件描述符的集合清空*/``    ``FD_ZERO(&rfds);``    ``/*把标准输入的文件描述符加入到集合中*/``    ``FD_SET(0, &rfds);``    ``maxfd = 0;``    ``/*把当前连接的文件描述符加入到集合中*/``    ``FD_SET(sock_cli, &rfds);``    ``/*找出文件描述符集合中最大的文件描述符*/`  `    ``if``(maxfd < sock_cli)``      ``maxfd = sock_cli;``    ``/*设置超时时间*/``    ``tv.tv_sec = 10;``    ``tv.tv_usec = 0;``    ``/*等待聊天*/``    ``retval = select(maxfd+1, &rfds, NULL, NULL, &tv);``    ``if``(retval == -1){``      ``printf``(``"select出错,客户端程序退出\n"``);``      ``break``;``    ``}``else` `if``(retval == 0){``      ``printf``(``"客户端没有任何输入信息,并且服务器也没有信息到来,waiting...\n"``);``      ``continue``;``    ``}``else``{``      ``/*服务器发来了消息*/``      ``if``(FD_ISSET(sock_cli,&rfds)){``        ``char` `recvbuf[BUFFER_SIZE];``        ``int` `len;``        ``len = recv(sock_cli, recvbuf, ``sizeof``(recvbuf),0);``        ``printf``(``"%s"``, recvbuf);``        ``memset``(recvbuf, 0, ``sizeof``(recvbuf));``      ``}``      ``/*用户输入信息了,开始处理信息并发送*/``      ``if``(FD_ISSET(0, &rfds)){``        ``char` `sendbuf[BUFFER_SIZE];``        ``fgets``(sendbuf, ``sizeof``(sendbuf), stdin);``        ``send(sock_cli, sendbuf, ``strlen``(sendbuf),0); ``//发送``        ``memset``(sendbuf, 0, ``sizeof``(sendbuf));``      ``}``    ``}``  ``}` `  ``close(sock_cli);``  ``return` `0;``}

  

以上就是良许教程网为各位朋友分享的Linux相关知识。

linux socket编程实例相关推荐

  1. linux c编程项目实例,Linux c编程实例_例子

    例一:字符与整型变量的实现 #include int main() { int c1,c2; char c3; c1='a'-'A'; c2='b'-'B'; c3='c'-; printf(&quo ...

  2. C语言 socket编程实例

    C语言 socket编程实例 一. 面向连接的流式套接字 C/S 例子 二. 非阻塞的多人聊天服务器端例子 三. 简单的 IPv6 UDP socket编程 四.使用wireshark抓包分析tcp协 ...

  3. Socket编程实例

    2019独角兽企业重金招聘Python工程师标准>>> Java具有非常强大的网络应用开发能力.学习网络编程,估计都是从编写一个socket通信开始,从中慢慢掌握网络编程的一点点知识 ...

  4. linux socket 编程

    socket  <script type="text/javascript"> </script> <script type="text/j ...

  5. Linux Socket编程

    IP socket 是在其上建立高级Internet 协议的最低级的层:从HTTP到SSL到POP3到Kerberos再到UDP-Time,每种Internet协议都建立在它的基础上.为了实现自定义的 ...

  6. android c++ socket编程,C++ 中 socket编程实例详解

    C++ 中 socket编程实例详解 sockets(套接字)编程有三种,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW):基于TCP的soc ...

  7. Linux网络编程实例详解

    本文介绍了在Linux环境下的socket编程常用函数用法及socket编程的一般规则和客户/服务器模型的编程应注意的事项和常遇问题的解决方法,并举了具体代 码实例.要理解本文所谈的技术问题需要读者具 ...

  8. socket编程实例_NIO之网络编程源码阅读

    之前分析了ByteBuffer.Channel相关的基本知识,现在对于NIO的基石已经有了基本的了解.不过NIO最突出的特性还是其基于select编程模型的网络编程体验. NIO网络编程通常有两种使用 ...

  9. Linux Socket编程入门——浅显易懂

    文章目录 1. 概述 2. Socket 3. 网络字节序 4. sockaddr 数据结构 5. 网络套接字API函数  5.1 socket()  5.2 bind()  5.3 listen() ...

最新文章

  1. 如何设置 homestead zhong redis 开机自启_CentOS安装Redis
  2. c语言dfs算法全排列代码,c语言dfs解决全排列问题
  3. c语言设计第4章答案,《C语言程序设计》第4章习题答案.doc
  4. 深入了解Zookeeper核心原理
  5. “产学合作勇创新·协同育人书新篇”贵州理工大数据学院数据科学训练营结题答辩报告会圆满举行...
  6. Windows 系统下安装anaconda教程 ,小白教程!!!
  7. 字节输入流-InputStream demo3
  8. 逼急了自己人都坑,腾讯内部上演吃鸡大战
  9. 【编辑器】用CodeRunner打造VScode的C++开发环境
  10. SQLite异常:unsafenativemethods.sqlite3_open_interop
  11. 网络安全--通过握手包破解WiFi(详细教程)
  12. matlab 平滑曲线连接_MATLAB数据可视化
  13. alsa mixer编程
  14. 根据银行卡号获取对应的银行信息
  15. 百度有啊转型生活平台 启用新标与框计算对接
  16. NN中的学习技巧之(一)参数的最优化之 Momentum
  17. 用Python写个自动批改作业系统~
  18. 2021-07-16思考-资本源于贪婪(与人性抗争)
  19. 僵尸进程zombie与孤儿进程orphan
  20. CSP基础-CSP入门简介

热门文章

  1. JAVA毕设项目沙县小吃点餐系统(java+VUE+Mybatis+Maven+Mysql)
  2. 解决vscode c++ 无法跳转代码(区别于大部分网上的解决方案)
  3. 1998世界杯主题曲[生命之杯]Ricky.Martin
  4. 200行代码实现马赛克拼图
  5. Matlab常用代码---持续更新
  6. 三菱FX3U与台达VFD M变频器通讯教程
  7. 3D目标检测(单目)D4LCN论文复现(paddlepaddle)
  8. 汽车理论matlab编程,汽车理论课后作业matlab编程详解(带注释)[试题学习]
  9. 网站一个月的花费是多少钱?
  10. Deep learning 译文 -- 第二部分