Home>

Socket multiplayer chat program C language version (1) Address:/go.php?id=94938&s=a

1v1 is implemented, and 1v more is easy.However, compared to the 1v1 program, I have undergone a major change.Adopt linked list for dynamic management.This really improves the efficiency a lot,At least the CPU usage is steadily below 20, and it will not soar to 100. Writing this in C is quite time consuming,Because you have to write any functional functions yourself,Unlike the C++ stl library,mfc is even easier to write,Next I will also update the mfc version of the multiplayer chat program.Okay, nonsense,going in the topic.

The problems to be solved by this program are as follows:

1. Cpu usage skyrocketing issues->Dynamic management with linked lists

2. User-defined chat,Just want to chat with whom->_A new chatname field is added to the client structure to indicate who to chat with.This field is important.Because the server forwards the message according to this field.

3. Substitutions and chats,Just talking,Want to talk to others,And I can also receive messages from other people->This is a small change to the client code,You can insert a piece of code before sending a chat message,Used to switch chat users.To do this,Use the getch () function to read the esc key. If the user presses this key,It means that i want to switch users,A line of prompt will be output,Please enter chat name, the name of the person i want to chat with,Add an identifier before sending this name over,Indicates that this message is a switch chat user message.After receiving the message, the server will determine whether the first character is an identifier.The second character cannot be an identifier.Find the currently online user based on this name,Then modify the chatname of the user who wants to switch chat to the user name.(Maybe a little bit around,If you don't understand the code, it will be clear and easy to understand ~)

4. Remind each other after offline->Still the same old routine,As long as the other party fails to send the other party offline.

Writing environment:win10, vs2015

Effect picture:

For the sake of convenience, the virtual machine is not used for demonstration.But it is definitely possible in a virtual machine,It should be said that as long as it is a local area network,If you can ping each other, you can use this program.

server code:

Linked list header file:

#ifndef _client_link_list_h_
#define _client_link_list_h_
#include<winsock2.h>
#include<stdio.h>
//Client information structure
typedef struct _client
{
 socket sclient;//Client socket
 char buf [128];//data buffer
 char username [16];//Client username
 char ip [20];//Client IP
 unsigned short port;//Client port
 uint_ptr flag;//Mark the client,Used to distinguish different clients
 char chatname [16];//Specify which client to chat with
 _client * next;//point to the next node
} client, * pclient;
/* * function Initialize linked list * return No return value * /
void init ();
/* * function Get head node * return Return head node * /
pclient getheadnode ();
/* * function Add a client * param client means a client object * return No return value * /
void addclient (pclient client);
/* * function delete a client * param flag identifies a client object * return returns true if the deletion was successful,false means failure * /
bool removeclient (uint_ptr flag);
/* * function finds the specified client by name * param name is the username of the specified client * return returns a client to indicate that the search was successful,Returning invalid_socket means that there is no such user * /
socket findclient (char * name);
/* * function finds the specified client based on the socket * param client is the socket of the specified client * return returns a pclient indicating that the search was successful,Returning null means there is no such user * /
pclient findclient (socket client);
/* * function Calculate the number of client connections * param client represents a client object * return Returns the number of connections * /
int countcon ();
/* * function Clear the linked list * return No return value * /
void clearclient ();
/* * function Check connection status and close a connection * return Return value * /
void checkconnection ();
/* * function specifies which client to send to * param fromname, sender * param toname, recipient * param data, message sent * /
void senddata (char * fromname, char * toname, char * data);
#endif //_ client_link_list_h_

Linked list cpp file:

#include "clientlinklist.h"
pclient head=(pclient) malloc (sizeof (_client));//Create a head node
/* * function Initialize linked list * return No return value * /
void init ()
{
 head->next=null;
}
/* * function Get head node * return Return head node * /
pclient getheadnode ()
{
 return head;
}
/* * function Add a client * param client means a client object * return No return value * /
void addclient (pclient client)
{
 client->next=head->next;//For example:head->1->2, then add a 3 and come in
 head->next=client;//3->>1->2, head->>3->>1->2
}
/* * function delete a client * param flag identifies a client object * return returns true if the deletion was successful,false means failure * /
bool removeclient (uint_ptr flag)
{
 //traverse from the beginning,Compare one by one
 pclient pcur=head->next;//pcur points to the first node
 pclient ppre=head;//ppre points to head
 while (pcur)
 {
  //head->1->2->3->4, to delete 2, just let 1->3
  if (pcur->flag == flag)
  {
   ppre->next=pcur->next;
   closesocket (pcur->sclient);//close the socket
   free (pcur);//Free the node
   return true;
  }
  ppre=pcur;
  pcur=pcur->next;
 }
 return false;
}
/* * function finds the specified client * param name is the username of the specified client * return returns a socket indicating that the search was successful,Returning invalid_socket means that there is no such user * /
socket findclient (char * name)
{
 //traverse from the beginning,Compare one by one
 pclient pcur=head;
 while (pcur=pcur->next)
 {
  if (strcmp (pcur->username, name) == 0)
   return pcur->sclient;
 }
 return invalid_socket;
}
/* * function finds the specified client based on the socket * param client is the socket of the specified client * return returns a pclient indicating that the search was successful,Returning null means there is no such user * /
pclient findclient (socket client)
{
 //traverse from the beginning,Compare one by one
 pclient pcur=head;
 while (pcur=pcur->next)
 {
  if (pcur->sclient == client)
   return pcur;
 }
 return null;
}
/* * function Calculate the number of client connections * param client represents a client object * return Returns the number of connections * /
int countcon ()
{
 int icount=0;
 pclient pcur=head;
 while (pcur=pcur->next)
  icount ++;
 return icount;
}
/* * function Clear the linked list * return No return value * /
void clearclient ()
{
 pclient pcur=head->next;
 pclient ppre=head;
 while (pcur)
 {
  //head->1->2->3->4, first delete 1, head->2, then free 1
  pclient p=pcur;
  ppre->next=p->next;
  free (p);
  pcur=ppre->next;
 }
}
/* * function Check connection status and close a connection * return Return value * /
void checkconnection ()
{
 pclient pclient=getheadnode ();
 while (pclient=pclient->next)
 {
  if (send (pclient->sclient, "", sizeof (""), 0) == socket_error)
  {
   if (pclient->sclient!=0)
   {
    printf ("disconnect from ip:%s, username:%s \ n", pclient->ip, pclient->username);
    char error [128]={0};//Send offline message to the sender
    sprintf (error, "the%s was downline. \ n", pclient->username);
    send (findclient (pclient->chatname), error, sizeof (error), 0);
    closesocket (pclient->sclient);//Simple judgment here:If sending a message fails,The connection is considered to be interrupted (there are multiple reasons) and the socket is closed
    removeclient (pclient->flag);
    break;
   }
  }
 }
}
/* * function specifies which client to send to * param fromname, sender * param toname, recipient * param data, message sent * /
void senddata (char * fromname, char * toname, char * data)
{
 socket client=findclient (toname);//Find if there is this user
 char error [128]={0};
 int ret=0;
 if (client!=invalid_socket&&strlen (data)!=0)
 {
  char buf [128]={0};
  sprintf (buf, "%s:%s", fromname, data);//add the username of the user
  ret=send (client, buf, sizeof (buf), 0);
 }
 else //Send an error message to the person who sent the message
 {
  if (client == invalid_socket)
   sprintf (error, "the%s was downline. \ n", toname);
  else
   sprintf (error, "send to%s message not allow empty, please try again! \ n", toname);
  send (findclient (fromname), error, sizeof (error), 0);
 }
 if (ret == socket_error) //Send offline message to the sender
 {
  sprintf (error, "the%s was downline. \ n", toname);
  send (findclient (fromname), error, sizeof (error), 0);
 }
}

server cpp:

/*
#include<winsock2.h>
#include<process.h>
#include<stdlib.h>
#include "clientlinklist.h"
#pragma comment (lib, "ws2_32.lib")
socket g_serversocket=invalid_socket;//server socket
sockaddr_in g_clientaddr={0};//Client address
int g_iclientaddrlen=sizeof (g_clientaddr);
typedef struct _send
{
 char fromname [16];
 char toname [16];
 char data [128];
} send, * psend;
//Send data thread
unsigned __stdcall threadsend (void * param)
{
 psend psend=(psend) param;//Convert to send type
 senddata (psend->fromname, psend->toname, psend->data);//Send data
 return 0;
}
//Accept data
unsigned __stdcall threadrecv (void * param)
{
 int ret=0;
 while (1)
 {
  pclient pclient=(pclient) param;
  if (! pclient)
   return 1;
  ret=recv (pclient->sclient, pclient->buf, sizeof (pclient->buf), 0);
  if (ret == socket_error)
   return 1;
  if (pclient->buf [0] == "#"&&pclient-&buf [1]!="#") //#indicates that the user wants to designate another user to chat
  {
   socket socket=findclient (&pclient->buf [1]);//Verify if the client exists
   if (socket!=invalid_socket)
   {
    pclient c=(pclient) malloc (sizeof (_client));
    c=findclient (socket);//As long as the chatname is changed, it will be automatically sent to the specified user when sending a message
    memset (pclient->chatname, 0, sizeof (pclient->chatname));
    memcpy (pclient->chatname, c->username, sizeof (pclient->chatname));
   }
   else
    send (pclient->sclient, "the user have not online or not exits.", 64,0);
   continue;
  }
  psend psend=(psend) malloc (sizeof (_send));
  //Assign the sender's user name and the user and message to the structure,Then passed as a parameter into the process of sending messages
  memcpy (psend->fromname, pclient->username, sizeof (psend->fromname));
  memcpy (psend->toname, pclient->chatname, sizeof (psend->toname));
  memcpy (psend->data, pclient->buf, sizeof (psend->data));
  _beginthreadex (null, 0, threadsend, psend, 0, null);
  sleep (200);
 }
 return 0;
}
//Start receiving message thread
void startrecv ()
{
 pclient pclient=getheadnode ();
 while (pclient=pclient->next)
  _beginthreadex (null, 0, threadrecv, pclient, 0, null);
}
//Manage connection
unsigned __stdcall threadmanager (void * param)
{
 while (1)
 {
  checkconnection ();//Check the connection status
  sleep (2000);//2s check once
 }
 return 0;
}
//Accept the request
unsigned __stdcall threadaccept (void * param)
{
 _beginthreadex (null, 0, threadmanager, null, 0, null);
 init ();//Initialization must not be done in while, otherwise the head will always be null! !! !!
 while (1)
 {
  //Create a new client object
  pclient pclient=(pclient) malloc (sizeof (_client));
  //If a client requests a connection, accept the connection
  if ((pclient->sclient=accept (g_serversocket, (sockaddr *)&g_clientaddr,&g_iclientaddrlen)) == invalid_socket)
  {
   printf ("accept failed with error code:%d \ n", wsagetlasterror ());
   closesocket (g_serversocket);
   wsacleanup ();
   return -1;
  }
  recv (pclient->sclient, pclient->username, sizeof (pclient->username), 0);//Receive username and username of the specified chat object
  recv (pclient->sclient, pclient->chatname, sizeof (pclient->chatname), 0);
  memcpy (pclient->ip, inet_ntoa (g_clientaddr.sin_addr), sizeof (pclient->ip));//Record client ip
  pclient->flag=pclient->sclient;//different socks have different numbers of uint_ptr
  pclient->port=htons (g_clientaddr.sin_port);
  addclient (pclient);//Add a new client to the linked list
  printf ("successfuuly got a connection from ip:%s, port:%d, uername:%s, chatname:%s \ n",   pclient->ip, pclient->port, pclient->username, pclient->chatname);
  if (countcon ()>= 2) //Forward message only after at least two users are connected to the server
   startrecv ();
  sleep (2000);
 }
 return 0;
}
//start the server
int startserver ()
{
 //Structure of storing socket information
 wsadata wsadata={0};
 sockaddr_in serveraddr={0};//server address
 ushort uport=18000;//The server listens on the port
 //Initialize the socket
 if (wsastartup (makeword (2, 2),&wsadata))
 {
  printf ("wsastartup failed with error code:%d \ n", wsagetlasterror ());
  return -1;
 }
 //Judge the version
 if (lobyte (wsadata.wversion)!=2 || hibyte (wsadata.wversion)!=2)
 {
  printf ("wversion was not 2.2 \ n");
  return -1;
 }
 //Create a socket
 g_serversocket=socket (af_inet, sock_stream, ipproto_tcp);
 if (g_serversocket == invalid_socket)
 {
  printf ("socket failed with error code:%d \ n", wsagetlasterror ());
  return -1;
 }
 //Set the server address
 serveraddr.sin_family=af_inet;//connection method
 serveraddr.sin_port=htons (uport);//Server listening port
 serveraddr.sin_addr.s_un.s_addr=htonl (inaddr_any);//any client can connect to this server
 //Binding server
 if (socket_error == bind (g_serversocket, (sockaddr *)&serveraddr, sizeof (serveraddr)))
 {
  printf ("bind failed with error code:%d \ n", wsagetlasterror ());
  closesocket (g_serversocket);
  return -1;
 }
 //Set the number of listening client connections
 if (socket_error == listen (g_serversocket, 20000))
 {
  printf ("listen failed with error code:%d \ n", wsagetlasterror ());
  closesocket (g_serversocket);
  wsacleanup ();
  return -1;
 }
 _beginthreadex (null, 0, threadaccept, null, 0, 0);
 for (int k=0;k<100;k ++) //Let the main thread sleep,Do not let it close the TCP connection.
  sleep (10000000);
 //Close the socket
 clearclient ();
 closesocket (g_serversocket);
 wsacleanup ();
 return 0;
}
int main ()
{
 startserver ();//Start the server
 return 0;
}

client code:

#define _winsock_deprecated_no_warnings
#include<winsock2.h>
#include<process.h>
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#pragma comment (lib, "ws2_32.lib")
#define recv_over 1
#define recv_yet 0
char username [16]={0};
char chatname [16]={0};
int istatus=recv_yet;
//Accept data
unsigned __stdcall threadrecv (void * param)
{
 char buf [128]={0};
 while (1)
 {
  int ret=recv (* (socket *) param, buf, sizeof (buf), 0);
  if (ret == socket_error)
  {
   sleep (500);
   continue;
  }
  if (strlen (buf)!=0)
  {
   printf ("%s \ n", buf);
   istatus=recv_over;
  }
  else
   sleep (100);
 }
 return 0;
}
//send data
unsigned __stdcall threadsend (void * param)
{
 char buf [128]={0};
 int ret=0;
 while (1)
 {
  int c=getch ();
  if (c == 27) //esc ascii is 27
  {
   memset (buf, 0, sizeof (buf));
   printf ("please input the chat name:");
   gets_s (buf);
   char b [17]={0};
   sprintf (b, "#%s", buf);
   ret=send (* (socket *) param, b, sizeof (b), 0);
   if (ret == socket_error)
    return 1;
   continue;
  }
  if (c == 72 || c == 0 || c == 68) //In order to display beautifully,Add a read character function without echo
   continue;//getch return value I experimentally concluded that if these values ​​are returned,Then getch will automatically skip,I don't understand.
  printf ("%s:", username);
  gets_s (buf);
  ret=send (* (socket *) param, buf, sizeof (buf), 0);
  if (ret == socket_error)
   return 1;
 }
 return 0;
}
//connect to the server
int connectserver ()
{
 wsadata wsadata={0};//Storage socket information
 socket clientsocket=invalid_socket;//Client socket
 sockaddr_in serveraddr={0};//server address
 ushort uport=18000;//Server port
 //Initialize the socket
 if (wsastartup (makeword (2, 2),&wsadata))
 {
  printf ("wsastartup failed with error code:%d \ n", wsagetlasterror ());
  return -1;
 }
 //Judging the socket version
 if (lobyte (wsadata.wversion)!=2 || hibyte (wsadata.wversion)!=2)
 {
  printf ("wversion was not 2.2 \ n");
  return -1;
 }
 //Create a socket
 clientsocket=socket (af_inet, sock_stream, ipproto_tcp);
 if (clientsocket == invalid_socket)
 {
  printf ("socket failed with error code:%d \ n", wsagetlasterror ());
  return -1;
 }
 //Enter the server IP
 printf ("please input server ip:");
 char ip [32]={0};
 gets_s (ip);
 //Set the server address
 serveraddr.sin_family=af_inet;
 serveraddr.sin_port=htons (uport);//Server port
 serveraddr.sin_addr.s_un.s_addr=inet_addr (ip);//Server address
 printf ("connecting ... \ n");
 //connect to the server
 if (socket_error == connect (clientsocket, (sockaddr *)&serveraddr, sizeof (serveraddr)))
 {
  printf ("connect failed with error code:%d \ n", wsagetlasterror ());
  closesocket (clientsocket);
  wsacleanup ();
  return -1;
 }
 printf ("connecting server successfully ip:%s port:%d \ n",  ip, htons (serveraddr.sin_port));
 printf ("please input your username:");
 gets_s (username);
 send (clientsocket, username, sizeof (username), 0);
 printf ("please input the chatname:");
 gets_s (chatname);
 send (clientsocket, chatname, sizeof (chatname), 0);
 printf ("\ n \ n");
 _beginthreadex (null, 0, threadrecv,&clientsocket, 0, null);//Start receiving and sending message threads
 _beginthreadex (null, 0, threadsend,&clientsocket, 0, null);
 for (int k=0;k<1000;k ++)
  sleep (10000000);
 closesocket (clientsocket);
 wsacleanup ();
 return 0;
}
int main ()
{
 connectserver ();//Connect to the server
 return 0;
}

Finally, the following points need to be improved:

1. No message record,So it's best to use file or database to record,Personal recommendation database.

2. No user registration,Login operation,Also use files or databases to get it.Read the database information as soon as the program runs.

3. The group chat function is not made,This is actually very simple,That is, the server forwards the received message to all online users regardless of 3721.

4. No offline messages,This uses a database to store offline messages,Then the user can send it immediately after going online.

In conclusion,The chat program without a database is really simple. The program written in C should pay attention to the operation of memory.There is also a TCP connection that takes too much time and memory (when the number of users reaches).

c
  • Previous Android custom control implements the bottom menu (top)
  • Next node + experss to crawl movie heaven crawler