linux socket编程实例
阅读目录
- 基本的局域网聊天
- 客户端服务端双向异步聊天源码
- 局域网内服务端和有限个客户端聊天源码
- 完美异步聊天服务端和客户端源码
- 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编程实例相关推荐
- linux c编程项目实例,Linux c编程实例_例子
例一:字符与整型变量的实现 #include int main() { int c1,c2; char c3; c1='a'-'A'; c2='b'-'B'; c3='c'-; printf(&quo ...
- C语言 socket编程实例
C语言 socket编程实例 一. 面向连接的流式套接字 C/S 例子 二. 非阻塞的多人聊天服务器端例子 三. 简单的 IPv6 UDP socket编程 四.使用wireshark抓包分析tcp协 ...
- Socket编程实例
2019独角兽企业重金招聘Python工程师标准>>> Java具有非常强大的网络应用开发能力.学习网络编程,估计都是从编写一个socket通信开始,从中慢慢掌握网络编程的一点点知识 ...
- linux socket 编程
socket <script type="text/javascript"> </script> <script type="text/j ...
- Linux Socket编程
IP socket 是在其上建立高级Internet 协议的最低级的层:从HTTP到SSL到POP3到Kerberos再到UDP-Time,每种Internet协议都建立在它的基础上.为了实现自定义的 ...
- android c++ socket编程,C++ 中 socket编程实例详解
C++ 中 socket编程实例详解 sockets(套接字)编程有三种,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW):基于TCP的soc ...
- Linux网络编程实例详解
本文介绍了在Linux环境下的socket编程常用函数用法及socket编程的一般规则和客户/服务器模型的编程应注意的事项和常遇问题的解决方法,并举了具体代 码实例.要理解本文所谈的技术问题需要读者具 ...
- socket编程实例_NIO之网络编程源码阅读
之前分析了ByteBuffer.Channel相关的基本知识,现在对于NIO的基石已经有了基本的了解.不过NIO最突出的特性还是其基于select编程模型的网络编程体验. NIO网络编程通常有两种使用 ...
- Linux Socket编程入门——浅显易懂
文章目录 1. 概述 2. Socket 3. 网络字节序 4. sockaddr 数据结构 5. 网络套接字API函数 5.1 socket() 5.2 bind() 5.3 listen() ...
最新文章
- 如何设置 homestead zhong redis 开机自启_CentOS安装Redis
- c语言dfs算法全排列代码,c语言dfs解决全排列问题
- c语言设计第4章答案,《C语言程序设计》第4章习题答案.doc
- 深入了解Zookeeper核心原理
- “产学合作勇创新·协同育人书新篇”贵州理工大数据学院数据科学训练营结题答辩报告会圆满举行...
- Windows 系统下安装anaconda教程 ,小白教程!!!
- 字节输入流-InputStream demo3
- 逼急了自己人都坑,腾讯内部上演吃鸡大战
- 【编辑器】用CodeRunner打造VScode的C++开发环境
- SQLite异常:unsafenativemethods.sqlite3_open_interop
- 网络安全--通过握手包破解WiFi(详细教程)
- matlab 平滑曲线连接_MATLAB数据可视化
- alsa mixer编程
- 根据银行卡号获取对应的银行信息
- 百度有啊转型生活平台 启用新标与框计算对接
- NN中的学习技巧之(一)参数的最优化之 Momentum
- 用Python写个自动批改作业系统~
- 2021-07-16思考-资本源于贪婪(与人性抗争)
- 僵尸进程zombie与孤儿进程orphan
- CSP基础-CSP入门简介
热门文章
- JAVA毕设项目沙县小吃点餐系统(java+VUE+Mybatis+Maven+Mysql)
- 解决vscode c++ 无法跳转代码(区别于大部分网上的解决方案)
- 1998世界杯主题曲[生命之杯]Ricky.Martin
- 200行代码实现马赛克拼图
- Matlab常用代码---持续更新
- 三菱FX3U与台达VFD M变频器通讯教程
- 3D目标检测(单目)D4LCN论文复现(paddlepaddle)
- 汽车理论matlab编程,汽车理论课后作业matlab编程详解(带注释)[试题学习]
- 网站一个月的花费是多少钱?
- Deep learning 译文 -- 第二部分