📄 tcpudpserver.cs
字号:
/* File : TcpUdpServer.cs
* Namespace : ClientServer
* Classes : TcpUdpServer
*/
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Timers;
namespace ClientServer
{
/* Class : TcpUdpServer
* Base Class : TcpListener
* Functionality : Handles all the server side functionalities including:
* 1. Creates a TcpListener to listen to incoming connection request;
* 2. Creates a UDP socket to handle all the call initiation messages like
* NOTIFY REQUEST and NOTIFY HANGUP REQUEST.
*/
public class TcpUdpServer : TcpListener
{
private Socket udpNotifySocket;
private Thread sampleTcpThread, udpNotifyThread;
private System.Timers.Timer receiveNotifyTimer;
private DateTime elapsedDateTime = DateTime.MinValue;
public String dataReceivedTcp;
public int tcpBytesReceived;
public TcpListener tcpListener;
public Socket tcpSocket;
public IPEndPoint remoteIpEndPoint;
public bool serverReceivedHangup = false;
public delegate void NotificationHandler();
public static event NotificationHandler peerNotify;
public static event NotificationHandler peerHangup;
public bool alreadyTalking = false;
public TcpUdpServer() : base(ClientServerConstants.SAMPLETCPPORT)
{
}
/* Function : StartListen()
* Purpose : This function will run as a thread to listen to the incoming
* TCP connection.
* Algorithm : It first starts the TcpListener (similar to calling
* Listen in the traditional Socket program). It will then call
* TcpListener.AcceptSocket() to "wait" for a client's connection.
* After that, it follows the traditional way to call
* SetSocketOption(), and Receive(). After receiving the data (a byte
* array) from the client, the byte array will be converted to a String by
* dataReceivedTcp = System.Text.Encoding.ASCII.GetString(received).
* "dataReceivedTcp" will then be used by the Windows Form application to
* display onto the TextBox control. When the client wants to close the
* connection, it will issue a "notifyLastMessage" to tell the server to
* close the socket after receiving this message. The while block would then
* return to TcpListener.AcceptSocket() to "wait" for another connection.
*
*/
public void StartListen()
{
try
{
this.Start();
while (true)
{
//This runs as a thread to listen to the incoming connections.
//The program will block on AcceptSocket() until a client connects.
tcpSocket = this.AcceptSocket();
while (true)
{
try
{
tcpSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, 7200);
}
catch (SocketException se)
{
MessageBox.Show("A Socket Exception has occurred!" + se.ToString(), "WinChat",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
if (tcpSocket.Connected)
{
Byte[] received = new Byte[ClientServerConstants.MESSAGESIZE];
tcpBytesReceived = tcpSocket.Receive(received, received.Length, SocketFlags.None);
dataReceivedTcp = System.Text.Encoding.ASCII.GetString(received);
//notifyLastMessage is sent to the server when the peer wants to hangup. The
//purpose of this last message is just to unblock the Receive call above.
if (String.Compare(dataReceivedTcp, ClientServerConstants.notifyLastMessage) == 0)
{
//Close the Socket for this connection.
tcpSocket.Close();
if (serverReceivedHangup == true)
{
//This message only gets printed on the side that receives the
//HANGUP REQUEST.
MessageBox.Show("The remote side has disconnected the call!", "WinChat",
MessageBoxButtons.OK);
serverReceivedHangup = false;
}
break;
}
}
}
}
}
catch (SocketException se)
{
//ErrorCode 10004 corresponds to the SocketException: A blocking operation was interrupted by
//a call to WSACancelBlockingCall.
if (se.ErrorCode != 10004)
{
MessageBox.Show("A Socket Exception has occurred!" + se.ToString(), "WinChat",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}
}
/* Function : receiveNotify()
* Purpose : This function will run as a thread to handle the incoming notifications
* sent from the peer (e.g. NOTIFY REQUEST and NOTIFY HANGUP). The
* notifications are handled by UDP connections because of their "come and
* go" nature.
* Algorithm : Since there is no UDP counterpart of TcpListener, I use the traditional
* Socket programming here to handle UDP connections. The following calls
* are made to setup the UDP server: Socket(), Bind(), ReceiveFrom(). When
* data (a byte array) is received, it will be converted to String by calling
* dataNotifyReceivedUdp = System.Text.Encoding.ASCII.GetString(received).
* The received String will then determine what the server should do next:
* 1. If the received String is "notifyRequest", the server will find out to
* see if the application is in "alreadyTalking" mode. If so, it won't
* accept this call request, and will return a message to let the remote
* peer know that the local user is "already talking to someone
* else, please try again later."
* If the server is not in "alreadyTalking" mode, the server will pop up
* a MessageBox to ask the called user if he/she wants to accept the call.
* If the called user answers "Yes", this UDP thread will invoke the delegate
* peerNotify(), so that the Windows Form application can be notified of
* the event, and take appropriate action (create a TcpClient to connect to
* the remote TcpListener). If the called user answers "No", the UDP server
* will simply return a negative notification to the calling user.
* It's noteworthy that a timer of 30 seconds will be started as soon as
* the "notifyRequest" is received. If the called user does not answer the
* question within 30 seconds, the timer will expire and the called user will
* be notified that he has missed a call, when he/she answers the question
* after 30 seconds.
* 2. If the received String is "notifyHangupRequest", the UDP server will return
* a "notifyHangupAccept" to the calling user. It will also invoke the
* delegate peerHangup() to notify the Windows Form application about the
* event, so that the Windows Form application will take the appropriate action
* (close the TcpClient).
*
*/
public void receiveNotify()
{
IPHostEntry localNotifyHostEntry;
try
{
//Create an UDP socket.
udpNotifySocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
localNotifyHostEntry = Dns.GetHostByName(Dns.GetHostName());
IPEndPoint localNotifyIpEndPoint = new IPEndPoint(localNotifyHostEntry.AddressList[0],
ClientServerConstants.UDPNOTIFYPORT);
EndPoint localNotifyEndPoint = (localNotifyIpEndPoint);
udpNotifySocket.Bind(localNotifyEndPoint);
//
while (true)
{
Byte[] received = new Byte[ClientServerConstants.NOTIFYMESSAGESIZE];
//This is just a dummy endpoint that will be replaced after the call to ReceiveFrom.
//In the ReceiveFrom call later on, dummyIpEndPoint will be passed by ref.
IPEndPoint dummyIpEndPoint = new IPEndPoint(localNotifyHostEntry.AddressList[0],
ClientServerConstants.UDPNOTIFYPORT);
EndPoint remoteNotifyEP = (dummyIpEndPoint);
int udpNotifyBytesReceived = udpNotifySocket.ReceiveFrom(received, ref remoteNotifyEP);
String dataNotifyReceivedUdp = System.Text.Encoding.ASCII.GetString(received);
//I could have used a switch() statement in the following code. however, for some
//reason the call to dataNotifyReceivedUdp.Equals(ClientServerConstants.notifyRequest)
//always returns false. I will investigate this problem further.
if (String.Compare(dataNotifyReceivedUdp, ClientServerConstants.notifyRequest) == 0)
{
if (alreadyTalking == true)
{
Byte[] returningByte = System.Text.Encoding.ASCII.GetBytes(
(ClientServerConstants.notifyAlreadyTalking).ToCharArray());
udpNotifySocket.SendTo(returningByte, remoteNotifyEP);
}
else
{
//The WinChatForm needs this remoteIpEndPoint to create a connection
//in the reverse direction.
remoteIpEndPoint = (IPEndPoint)remoteNotifyEP;
DialogResult dialogResult;
//This Notify Timer is started to make sure the called user can answer
//this call within 30 seconds, so that the calling user does not have
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -