Recently learn Linux socket programming, see UNP that book, by the Way, write a similar to the simplest chat function software, the interface is written in Qt, write down summary, if there is a problem, welcome everyone and I exchange.
The mode is the C/S mode, the server waits for the request, and the client sends the request. The connection is TCP is not UDP, in fact, the implementation of UDP is more simple.
I. Environmental construction
I use Docker to build a few virtual machines, the specific way you can refer to the online or I wrote a summary of Docker: Click to open the link
Docker is also very lightweight, much faster than a typical virtual machine.
After the installation of Docker is the basic first look at whether the network is connected, if you do not connect to look at the link, firewall set up a bit.
Two. No interface code implementation:
This part of the code mainly refer to the simple example on the UNP, which will certainly have bugs, because the complex TCP environment is not considered, but the simple implementation of the first:
Server-side:
#include "unp.h" #include <stdio.h> #include <unistd.h>void Answer (file *fop, file *fip, int sockfd) {int MAXFDP1, stdineof; Fd_set RSet; Char Buf[maxline]; int n; stdineof = 0; Fd_zero (&rset); for (;;) {if (stdineof = = 0) fd_set (Fileno (FIP), &rset); Fd_set (SOCKFD, &rset); MAXFDP1 = Max (Fileno (FIP), SOCKFD) + 1; Select (MAXFDP1, &rset, NULL, NULL, NULL); if (Fd_isset (SOCKFD, &rset)) {/* socket is readable */if ((n = Read (SOCKFD, buf, MAXLINE)) = = 0) { if (stdineof = = 1) return; /* Normal termination */else Err_quit ("Client quit!"); } Write (Fileno (FOP), buf, N); if (Fd_isset (Fileno (FIP), &rset)) {/* input is readable */if ((n = Read (Fileno (FIP), buf, MAXL INE)) = = 0) {stdineof = 1; Shutdown (SOCKFD, SHUT_WR); /* Send FIN */FD_CLR (Fileno (FIP), &rset); Continue } writen (SOCKFD, buf, N); }}}int Main (int argc, char **argv) {int listenfd, CONNFD; pid_t Childpid; Socklen_t Clilen; struct sockaddr_in cliaddr, servaddr; LISTENFD = Socket (af_inet, sock_stream, 0); Bzero (&servaddr, sizeof (SERVADDR)); servaddr.sin_family = af_inet; SERVADDR.SIN_ADDR.S_ADDR = htonl (Inaddr_any); Servaddr.sin_port = htons (Serv_port); Servaddr.sin_port = htons (50001); Bind (LISTENFD, (SA *) &servaddr, sizeof (SERVADDR)); Listen (LISTENFD, Listenq); for (;;) {Clilen = sizeof (CLIADDR); if (CONNFD = Accept (LISTENFD, (SA *) &cliaddr, &clilen)) < 0) {if (errno = = eintr) Continue /* Back to for () */Else Err_sys ("Accept error"); }if ((Childpid = Fork ()) = = 0) {/* child process */Close (LISTENFD); /* Close Listening socket */Answer (stdout, stdin, CONNFD); printf ("End of one connect!\n"); Exit (0); } Close (CONNFD); /* Parent Closes connected socket */}}
Client code:
#include "unp.h" const int SIZE = 1024;void Input (FILE *fp, int sockfd) {INTMAXFDP1, stdineof;fd_setrset;charbuf[maxline ];intn;stdineof = 0; Fd_zero (&rset); for (;;) {if (stdineof = = 0) fd_set (Fileno (FP), &rset); Fd_set (SOCKFD, &rset); MAXFDP1 = max (Fileno (FP), SOCKFD) + 1; Select (MAXFDP1, &rset, NULL, NULL, NULL), if (Fd_isset (SOCKFD, &rset)) {/* socket is readable */if ((n = Read (sock FD, BUF, MAXLINE)) = = 0) {if (stdineof = = 1) return;/* normal termination */elseerr_quit ("Str_cli:server terminated Premat Urely ");} Write (Fileno (stdout), buf, n);} if (Fd_isset (Fileno (FP), &rset)) {/* input is readable */if ((n = Read (Fileno (FP), buf, MAXLINE) = = 0) {stdineof = 1; Shutdown (SOCKFD, SHUT_WR);/* Send FIN */FD_CLR (Fileno (FP), &rset); continue;} Writen (SOCKFD, buf, n);}}} int main (int argc, char **argv) {intsockfd;struct sockaddr_inservaddr;if (argc! = 3) err_quit ("Usage:tcpcli <ipaddress >::<Port> "); sockfd = Socket (af_inet, sock_stream, 0); Bzero (&servadDr, sizeof (SERVADDR)); servaddr.sin_family = Af_inet;servaddr.sin_port = Htons (atoi (argv[2)); Inet_pton (Af_inet, argv [1], &servaddr.sin_addr); Connect (SOCKFD, (SA *) &servaddr, sizeof (SERVADDR)); struct sockaddr_in SS; socklen_t len = sizeof (ss); if (GetSockName (SOCKFD, (SA *) &ss, &len) < 0) return 0; int clientport = Ntohs (Ss.sin_port); Pay attention the Translate n<->pstr_cli (stdin, SOCKFD);/* Do it all */printf ("%d\n", ClientPort); Input (stdin, SOCKFD); exit (0);}
Vim a paste, this code format I was helpless ...
To run through this code, you have to configure the UNP environment. OK, I have the code below the interface can not configure the UNP environment
Three. Add interface
I abstracted a few base classes, the main interface is 4, first to look at the interface bar:
The server side and the client chat interface is the same, the server side I did a little processing, rejected the connection of multiple customers, when a customer chats, the listening socket is closed.
Just learning Qt started to interface, it is really disgusting.
Main code:
Client Login:
#include "clientstart.h" #include "chatclient.h" #include <iostream> #include <fstream> #include < Sstream>using namespace Std; Clientstart::clientstart (Qwidget *parent): startbase (parent) {this->classname = "clientstart"; /*belong to First layout*/qlabel *labeloppositeip = new Qlabel ("Please enter server IP:"); Oppositeip = new Qlineedit; Qlabel *labeloppositeport = new Qlabel ("Please enter Server port:"); Oppositeport = new Qlineedit; Firstlayout->addwidget (LABELOPPOSITEIP, 2, 0, 1, 1); Firstlayout->addwidget (OPPOSITEIP, 2, 1, 1, 4); Firstlayout->addwidget (Labeloppositeport, 3, 0, 1, 1); Firstlayout->addwidget (Oppositeport, 3, 1, 1, 4); /*second layout*/secondlayout = new Qhboxlayout; Secondlayout->setspacing (60); Secondlayout->addwidget (buttonsure); Secondlayout->addwidget (Buttoncancel); Toplayout->addlayout (firstlayout); Toplayout->addlayout (secondlayout); qwidget* widget = new Qwidget (this); Widget->setlAyout (toplayout); This->setcentralwidget (widgets); Add the Toplayout in current Dialog}clientstart::~clientstart () {//delete this;} void Clientstart::d oopen () {//qmessagebox::information (this, "Success", Qobject::tr ("xxx"), Qmessagebox::ok); ServerIP = (Oppositeip->text ()). Tostdstring (); ServerPort = (Oppositeport->text ()). Tostdstring (); /*start Connect now*/bool Isconnect = Tryconnect (); if (!isconnect) {Errorcall (this, "Connection failed"); return; } chatclient cc (ClientIP, ClientPort, ServerIP, ServerPort, servaddr, CLIENTSOCKETFD); Cc.exec (); Cc.show (); This->close ();} void Clientstart::d ocancel () {qmessagebox::information (this, "failed", QOBJECT::TR ("Please reset"), Qmessagebox::ok); Oppositeip->settext (""); Oppositeport->settext ("");} BOOL Clientstart::tryconnect () {CLIENTSOCKETFD = socket (af_inet, sock_stream, 0); Bzero (&servaddr, sizeof (servaddr )); servaddr.sin_family = Af_inet;servaddr.sin_port = Htons (atoI (Serverport.c_str ())); Inet_pton (Af_inet, Serverip.c_str (), &servaddr.sin_addr); if (:: Connect (CLIENTSOCKETFD, (SA *) &servaddr, sizeof (SERVADDR)) < 0) return 0; /*get client port, IP has already got*/struct sockaddr_in SS; socklen_t len = sizeof (ss); if (GetSockName (CLIENTSOCKETFD, (SA *) &ss, &len) < 0) return 0; StringStream stmp; change int to string stmp << Ntohs (ss.sin_port); Pay attention the translate n<->p ClientPort = Stmp.str (); return 1;}
Client Chat:
#include "chatclient.h" #include <iostream> #include <fstream>using namespace std; Chatclient::chatclient (std::string clientip, std::string clientport, std::string ServerIP, std::string serverPort, struct sockaddr_in servaddr, int clientsocketfd): Chatbase (NULL) {this->clientip = ClientIP; This->clientport = ClientPort; This->serverip = ServerIP; This->serverport = ServerPort; THIS->SERVADDR = servaddr; THIS->CLIENTSOCKETFD = CLIENTSOCKETFD; This->linelocalip->settext (QString (Clientip.c_str ())); This->linelocalport->settext (QString (Clientport.c_str ())); This->lineoppositeip->settext (QString (Serverip.c_str ())); This->lineoppositeport->settext (QString (Serverport.c_str ())); This->secondlayout->setreadonly (1); This->secondlayout->settext ("Start chatting now!\n"); This->secondlayout->append (ShowTime ()); This->secondlayout->show (); /*connect the socket and function*/stdineof = 0; Fd_zero (&rset); REVSN = new Qsocketnotifier (CLIENTSOCKETFD, Qsocketnotifier::read); Qobject::connect (REVSN, SIGNAL (activated (int)), this, SLOT (dodatareceived ()));} Chatclient::~chatclient () {stdineof = 1; Shutdown (CLIENTSOCKETFD, SHUT_WR);/* Send FIN */Fd_zero (&rset); This->close ();} void ChatClient::d osendmsg () {std::string sent = (Forthlayout->toplaintext ()). Tostdstring (); Sent + = ' \ n '; if (Write (CLIENTSOCKETFD, Sent.c_str (), Sent.size ()) < 0) {Errorcall (this, "Send Message failed!"); return; } appendsecondlayout (Sent); Forthlayout->clear ();} void ChatClient::d ocancel () {forthlayout->clear ();} void ChatClient::d osendfile () {}void chatclient::d ovedio () {}void chatclient::d odatareceived () {int revlen; if ((Revlen = Read (CLIENTSOCKETFD, buf, MAXLINE)) = = 0) {if (stdineof = 1) return;/* normal termination */else { Errorcall (this, "server terminated prematurely"); This->close (); }} Buf[revlen] = ' + '; Appendsecondlayout (String (BUF)); return;}
Server-side code is not released, basically is similar.
I uploaded all the code to CSDN, link: Click to open the link
Installation requires configuration of QT5, video and file features are not implemented
Four. Summary and future prospects
This software is also written to play, there must be a bug. In fact, the previous plan is to make NAT forwarding, so that the host between the two LAN can chat directly, and later found that it does not work, no permission to modify the network routing NAT rules.
Future plans are as follows:
1. It is not the QT interface, direct command line.
2. Implement file transfer.
3. Abandon the C/s architecture, engage in a server with public IP, two chat hosts at the same time connect to this service, the server to forward
If you have any questions welcome communication, hope to have a small partner with me to do ~ ~.
Although the code is not difficult, but also oneself Daoteng, reprint please specify address: Http://write.blog.csdn.net/postedit
Socket-based chat software under Linux