I've been working with an application that uses an SQL Anywhere 11 database. Like hopefully all SQL implementations, SQL Anywhere supports autoincrementing columns. Instead of taking advantage of that, though, this application maintains a table of the next available primary key value for each table. To generate IDs for new rows, it calls upon a stored procedure.
The application has been around for a long time, and I'm sure if the authors were developing it today, they would make a better choice. I've certainly rigged up systems that were weirdly engineered or not necessary. Something I've learned is that very general, very useful functionality is probably already implemented in the platform. If it's implemented in a well developed, widely used piece of software, it is likely a solid solution. That's not always the case, but it's at least worth checking over. Before deciding to reimplement something already done in the platform, you should have a specific reason, like performance or a missing capability.
Various technical articles, IT-related tutorials, software information, and development journals
Showing posts with label hl progamming. Show all posts
Showing posts with label hl progamming. Show all posts
Friday, June 1, 2018
Friday, May 12, 2017
Test mocks are definitely worth it
I'm currently working on an application that interacts with a system outside the machine it runs on - specifically, it sends and receives text messages through an Internet SMS gateway. That gets rather unwieldy in test environments, since obviously I can't use real people's data, and I only have one or two phones available to me at any given time. Besides, using the SMS gateway costs money.
It used to take me a while to get the SMS gateway connected for tests, and waiting for the texts to go through one direction and back the other also took up several moments, but it was never too much of an inconvenience. Then one day while planning a large round of tests on new features, I decided to finally sit down and write a mock for the gateway. Instead of actually sending the texts out to the Internet, this message provider (a drop-in replacement for the real library) just puts them up in a window. Instead of me needing to text back on a phone, I just type the "from" number and the text into some fields and click a button.
This is fast and exceptionally convenient compared to the old system. I wasn't sure before creating it whether it would be worthwhile, but it unquestionably was. Of course, this presupposes an architecture that allows easy drop-in replacement of the component being mocked, but if you have that infrastructure, strongly consider making a test mock.
It used to take me a while to get the SMS gateway connected for tests, and waiting for the texts to go through one direction and back the other also took up several moments, but it was never too much of an inconvenience. Then one day while planning a large round of tests on new features, I decided to finally sit down and write a mock for the gateway. Instead of actually sending the texts out to the Internet, this message provider (a drop-in replacement for the real library) just puts them up in a window. Instead of me needing to text back on a phone, I just type the "from" number and the text into some fields and click a button.
This is fast and exceptionally convenient compared to the old system. I wasn't sure before creating it whether it would be worthwhile, but it unquestionably was. Of course, this presupposes an architecture that allows easy drop-in replacement of the component being mocked, but if you have that infrastructure, strongly consider making a test mock.
Friday, September 19, 2014
The For-If Anti-Pattern
This isn't anything new in terms of coding advice (you can find stuff on the Internet about it everywhere), but I just caught myself using the for-if anti-pattern. The term anti-pattern refers to a coding style (a pattern) that is suboptimal or flawed in some way. For-if is an inefficiency that fails to leverage the given lookup or access mechanism and instead enumerates every single entry in a collection, checks to see whether it has the right ID, and then does something with it. In obvious terms:
For n = 0 to 300
If n = CInt(userChoice) Then
Console.WriteLine(messageTable(userChoice))
End If
Next
It's obvious here; the above code could be:
Console.WriteLine(messageTable(userChoice))
And the message table is accessed directly. I noticed that I had been doing this with the Properties collection of DirectoryEntry:
For Each p as PropertyValueCollection In GetCurDir.Properties
If p.PropertyName = Arguments(1) Then
val = p(0)
End If
Next
It hit me that I could have simply written:
val = GetCurDir.Properties(Arguments(1)).Value(0)
Moral of the story: check to see whether your collection supports direct accessing. You should definitely not be looping through a Dictionary to find a pair with a certain key and extract the value from the KeyValuePair - get the value directly by passing the key to its default property!
For n = 0 to 300
If n = CInt(userChoice) Then
Console.WriteLine(messageTable(userChoice))
End If
Next
It's obvious here; the above code could be:
Console.WriteLine(messageTable(userChoice))
And the message table is accessed directly. I noticed that I had been doing this with the Properties collection of DirectoryEntry:
For Each p as PropertyValueCollection In GetCurDir.Properties
If p.PropertyName = Arguments(1) Then
val = p(0)
End If
Next
It hit me that I could have simply written:
val = GetCurDir.Properties(Arguments(1)).Value(0)
Moral of the story: check to see whether your collection supports direct accessing. You should definitely not be looping through a Dictionary to find a pair with a certain key and extract the value from the KeyValuePair - get the value directly by passing the key to its default property!
Thursday, September 18, 2014
How to Actually Get the Active Directory Date Information out of __ComObject in .NET
If you're familiar with the .NET LDAP library (System.DirectoryServices), you may have noticed that some entries in the Properties collection for a given DirectoryEntry are instances of System.__ComObject. This is really inconvenient because there's no way to tell what type of thing such an object really is. Even when you know what it should be, it's kind of a pain to extract the data.
For instance, the pwdLastSet and lastLogon properties of user objects show up as __ComObject despite being date-time things. Specifically, they (and most AD objects that you expect to be date-times) are IADsLargeInteger. To convert them into normal DateTime objects, you need a reference to a COM interop DLL in your project, specifically the "Active DS Type Library".
Let's say you have a __ComObject that is supposed to be a date-time, I'll call it p. Use the following code to get it as a nice .NET DateTime:
Dim li As ActiveDs.IADsLargeInteger = p.Value
Return DateTime.FromFileTimeUtc((CLng(li.HighPart) << 32) + li.LowPart)
For instance, the pwdLastSet and lastLogon properties of user objects show up as __ComObject despite being date-time things. Specifically, they (and most AD objects that you expect to be date-times) are IADsLargeInteger. To convert them into normal DateTime objects, you need a reference to a COM interop DLL in your project, specifically the "Active DS Type Library".
Let's say you have a __ComObject that is supposed to be a date-time, I'll call it p. Use the following code to get it as a nice .NET DateTime:
Dim li As ActiveDs.IADsLargeInteger = p.Value
Return DateTime.FromFileTimeUtc((CLng(li.HighPart) << 32) + li.LowPart)
Monday, August 25, 2014
Collection Performance in .NET
I had to do a mock lab report for Chemistry class (it didn't have to be about chemistry, just use the normal scientific method and report style), so I quickly threw together a paper/experiment analyzing the performance of a few collections in .NET. It's obviously nothing shocking, but could possibly serve as a nice reference for combining the scientific method with computer science.
Download paper (PDF)
Download paper (PDF)
Monday, March 3, 2014
Restricted Interfaces, Please
In Abiathar's extension API, there are two or three interfaces that would mess everything up if implemented by something other than Abiathar Core. They need to be interfaces because I don't want the extensions to need a reference to Abiathar's internal details, which may change drastically. It wouldn't really be a problem unless objects of the interface types didn't have to be passed back through the API, which they do. (Level wrappers.)
Also, in F#, Clojure, and (maybe) Haskell, there is an Option type that may or may not be an instance of Some, which contains a real value. Option is also subclassed by None, which is kind of the entire point of being an option type. Now, it's possible to inherit Option with some other class that breaks all the logic. This can't be stopped because Option has to be inheritable by Some and None to have an option.
What I really want is some way to restrict which assemblies can implement an interface or inherit a type. I at first thought adding a line like "InheritableBy etc" to the definition would work to allow only those types to inherit it, but this significantly increases compiler/parser complexity. Also, it would be difficult to make it not break when used in different assemblies (would a reference to the original be needed? that would defeat the purpose).
Also, in F#, Clojure, and (maybe) Haskell, there is an Option type that may or may not be an instance of Some, which contains a real value. Option is also subclassed by None, which is kind of the entire point of being an option type. Now, it's possible to inherit Option with some other class that breaks all the logic. This can't be stopped because Option has to be inheritable by Some and None to have an option.
What I really want is some way to restrict which assemblies can implement an interface or inherit a type. I at first thought adding a line like "InheritableBy etc" to the definition would work to allow only those types to inherit it, but this significantly increases compiler/parser complexity. Also, it would be difficult to make it not break when used in different assemblies (would a reference to the original be needed? that would defeat the purpose).
Thursday, December 19, 2013
Super Inline Functions?
I did some more thinking about what would be awesome to have in .NET and remembered something about my new implementation of SupaChat Server. For a lot of the commands, it tries to find a group object for a group ID specified somewhere in the arguments array. Therefore, I have to have code like this in those case blocks:
Dim TargetGroup As Group = Groups.Find(Function(g) g.ID = _
Args(1))
If TargetGroup Is Nothing Then
Sender.SendData("*G* Group doesn't exist!")
Return
End If
If Not Sender.RepUser.InGroup(TargetGroup) Then
Sender.SendData("*G* You're not there!")
Return
End If
In addition to being simplified by the breaker subs I talked about last time, this is kind of a pain and is very repetitive. I would like to always define variables for things like the target group, target user, and sending rank and have them only set by one line - a "set-up" call if you will. However, that would require a new kind of inline sub. It's hard to explain, but I was thinking of something like this:
Breakable Sub HandleCommand(Command As String, Args() As String)
Dim TargetGroup As Group
' other variables
Inline Breaker(1) Sub GetGroup(ByInline TargetGroup As Group, _
ArgPos As Integer, NeedsPresent As Boolean)
TargetGroup = Groups.Find(Function(g) g.ID = Args(ArgPos))
' etc from code in last section
End Sub
Select Case UCase(Command)
Case "LEAVEGROUP"
GetGroup(1, True)
' other instructions
' other commands
End Select
' other stuff
End Sub
That "inline" sub inside the command handler sub is only visible and runnable in the context of that one sub. Variables passed ByInline pass a double-strong pointer, so setting where it points inside the inline sub would change what it points to back in the root sub. Inline subs must appear after all local variables they use. When invoking inline subs, ByInline parameters cannot be specified; they are taken from the current accessible variable set.
This would be an amazing feature. Get on it, Microsoft!
Dim TargetGroup As Group = Groups.Find(Function(g) g.ID = _
Args(1))
If TargetGroup Is Nothing Then
Sender.SendData("*G* Group doesn't exist!")
Return
End If
If Not Sender.RepUser.InGroup(TargetGroup) Then
Sender.SendData("*G* You're not there!")
Return
End If
In addition to being simplified by the breaker subs I talked about last time, this is kind of a pain and is very repetitive. I would like to always define variables for things like the target group, target user, and sending rank and have them only set by one line - a "set-up" call if you will. However, that would require a new kind of inline sub. It's hard to explain, but I was thinking of something like this:
Breakable Sub HandleCommand(Command As String, Args() As String)
Dim TargetGroup As Group
' other variables
Inline Breaker(1) Sub GetGroup(ByInline TargetGroup As Group, _
ArgPos As Integer, NeedsPresent As Boolean)
TargetGroup = Groups.Find(Function(g) g.ID = Args(ArgPos))
' etc from code in last section
End Sub
Select Case UCase(Command)
Case "LEAVEGROUP"
GetGroup(1, True)
' other instructions
' other commands
End Select
' other stuff
End Sub
That "inline" sub inside the command handler sub is only visible and runnable in the context of that one sub. Variables passed ByInline pass a double-strong pointer, so setting where it points inside the inline sub would change what it points to back in the root sub. Inline subs must appear after all local variables they use. When invoking inline subs, ByInline parameters cannot be specified; they are taken from the current accessible variable set.
This would be an amazing feature. Get on it, Microsoft!
Wednesday, December 18, 2013
Force Multiple Returns - "Breakers"
I would really, really like a way in .NET to unwind the stack twice in a return-like statement. This would be exceedingly useful in request handling methods that check some conditions and exit the big handler if the check fails. There is currently no way to do this without inserting "if check is false then exit" everywhere. It's even more of a pain in ASP .NET applications that require the request to be aborted if the page will not be fully served.
Sub Page_Load() Handles Me.Load
If Not IsUserAdmin(Session("UserID")) Then
Response.Redirect("PermissionDenied.html")
Exit Sub
End If
' actual page loading stuff
End Sub
Here, it would be excellent if the check function could cancel the Page_Load routine. (It might be necessary to pass the Response variable to an auth function.) I was thinking something like this:
Breaker(1) Sub EnsureUserAdmin(ID As Integer, _
Handler As HttpResponse)
If Not IsUserAdmin(ID) Then
Handler.Redirect("PermissionDenied.html")
Break 1
End If
End Sub
Breakable Sub Page_Load() Handles Me.Load
EnsureUserAdmin(Session("UserID"), Response)
' administrative things
End Sub
The parameter to the Breaker keyword specifies how many frames up the stack the method can break. The Breakable keyword flags the method as being able to use breaker methods.
I realize it's kind of redundant for void returners as they could be changed into Booleans, but for functions that can actually have a useful output, it would be a real pain to use a composite/nullable structure. Please, Microsoft?
Sub Page_Load() Handles Me.Load
If Not IsUserAdmin(Session("UserID")) Then
Response.Redirect("PermissionDenied.html")
Exit Sub
End If
' actual page loading stuff
End Sub
Here, it would be excellent if the check function could cancel the Page_Load routine. (It might be necessary to pass the Response variable to an auth function.) I was thinking something like this:
Breaker(1) Sub EnsureUserAdmin(ID As Integer, _
Handler As HttpResponse)
If Not IsUserAdmin(ID) Then
Handler.Redirect("PermissionDenied.html")
Break 1
End If
End Sub
Breakable Sub Page_Load() Handles Me.Load
EnsureUserAdmin(Session("UserID"), Response)
' administrative things
End Sub
The parameter to the Breaker keyword specifies how many frames up the stack the method can break. The Breakable keyword flags the method as being able to use breaker methods.
I realize it's kind of redundant for void returners as they could be changed into Booleans, but for functions that can actually have a useful output, it would be a real pain to use a composite/nullable structure. Please, Microsoft?
Tuesday, December 17, 2013
Watch out for Shadows
While messing around with types for a personal project, I ran into some unusual problems involving the Shadows operator. For background, let me explain that I had two classes, one of which extended the other, each of which containing an identically named class that would hold additional data for it. I wanted one class to override the other in the derived class, thereby swapping out the data type of that extra storage field. Since classes can't be declared Overloads, I settled for Shadows, which seems to have the same effect. I applied it to both the class and the field.
But it doesn't! Members declared Shadows will only be called if the invocation is on an object that is strictly known to be of the derived type! I was actually declaring two fields with an identical name but different data types. When I used the polymorphic approach and simply read from the field, I was only accessing the field declared by the superclass, never the subclass.
Shadows is not the same as Overloads. Make sure you know you're creating two different members when using it. In my case, the issue was solved by changing the field to a property and giving the conflicting members (both class and field) different names. Then, I could just have the subclass report its own field of its own data type.
But it doesn't! Members declared Shadows will only be called if the invocation is on an object that is strictly known to be of the derived type! I was actually declaring two fields with an identical name but different data types. When I used the polymorphic approach and simply read from the field, I was only accessing the field declared by the superclass, never the subclass.
Shadows is not the same as Overloads. Make sure you know you're creating two different members when using it. In my case, the issue was solved by changing the field to a property and giving the conflicting members (both class and field) different names. Then, I could just have the subclass report its own field of its own data type.
Saturday, December 7, 2013
The CLI/CIL/CLS/CTS Specification
Most .NET programmers aren't aware that it's compatible with other languages that we don't usually consider part of that family, like Mono. This is because its written assemblies and execution environment are standard - set by Microsoft and quite a few other companies. The spec provides details on the Common Language Specification, the Common Intermediary Language, the Common Type System, in addition to a lot of helpful tips for programmers. The thing, at its heart, is comprised of a list of rules for compilers and translators that, if followed, guarantee security and compatibility. The ECMA also packages an XML file than contains information on all necessary base classes.
ECMA page
Direct PDF link
Mirrored BCL XML
ECMA page
Direct PDF link
Mirrored BCL XML
Thursday, December 5, 2013
F#
I saw the name of F# in my Visual Studio add-ons dialog today and it made me remember how interesting that language is. I can't write anything it it without extensively using reference material, but it appears very powerful and very well integrated with the CLI. A while ago, I thought it was Microsoft's answer to R, but that does not appear to be the case. It is more like a .NET-compliant version of Haskell with more object-oriented features.
Some of its most interesting abilities are its handlings of the functional pattern. Types are handled in a way less painful than in VB and even more flexibly than in C#. However, the nature of variables and assignments to them seems a little bit strange, at least from my VB perspective.
Who should use F#? I'm not sure, to be honest. It does look like a good language to know should you ever stumble upon an amazing possible use of it.
Some of its most interesting abilities are its handlings of the functional pattern. Types are handled in a way less painful than in VB and even more flexibly than in C#. However, the nature of variables and assignments to them seems a little bit strange, at least from my VB perspective.
Who should use F#? I'm not sure, to be honest. It does look like a good language to know should you ever stumble upon an amazing possible use of it.
Monday, December 2, 2013
.NET - Writing Your Own .NET Compiler
Reflection in .NET is exceedingly easy. It's also incredibly versatile; in fact, it's possible to create new classes, methods, and other fancy stuff with the *Builder classes. Add those to an Assembly and you've got yourself an entire library in memory. There's one method that makes it all even more amazing: the Save method on AssemblyBuilder. It will take all the dynamically generated code and dump it into a legit .NET library on your hard drive. I think you can even make DLLs by specifying the appropriate options in AppDomain.DefineDynamicAssembly.
So, if you're good at using ILGenerator, you can take in a text file, do whatever parsing and understanding you need, create the appropriate classes and method bodies on the fly, and create a new CLI-compliant executable. This is, essentially, a compiler. Learn more by reading the documentation on the types in System.Reflection!
So, if you're good at using ILGenerator, you can take in a text file, do whatever parsing and understanding you need, create the appropriate classes and method bodies on the fly, and create a new CLI-compliant executable. This is, essentially, a compiler. Learn more by reading the documentation on the types in System.Reflection!
Wednesday, November 27, 2013
Creating Extension Methods in VB .NET
An "extension method" is a method of a class or interface that is actually defined in a different module, usually by a different author. They are usually used to "extend" the functionality of existing types without creating new ones.
In C#, creating extension methods is pretty easy. Simply put a "this" before the type on the first argument, which indicates that it is the instance on which the method is running. In VB, it's slightly different but still pretty simple. First, import System.Runtime.CompilerServices at the top of the file containing the module. Then, apply the <Extension> attribute to the Sub or Function. When writing the method body, write as if the first argument is the current object. It obviously has to be the same type as the type to which you'd like to apply the extension.
Here's one I like to add to the string type:
Imports System.Runtime.CompilerServices
Module Extensions
<Extension> Public Function EqCaseless(This As String, _
Other As String) As Boolean
Return String.Equals(This, Other, _
StringComparison.InvariantCultureIgnoreCase)
End Sub
End Module
Then, I can compare strings caselessly like this:
If input.EqCaseless("splosions") Then End
In C#, creating extension methods is pretty easy. Simply put a "this" before the type on the first argument, which indicates that it is the instance on which the method is running. In VB, it's slightly different but still pretty simple. First, import System.Runtime.CompilerServices at the top of the file containing the module. Then, apply the <Extension> attribute to the Sub or Function. When writing the method body, write as if the first argument is the current object. It obviously has to be the same type as the type to which you'd like to apply the extension.
Here's one I like to add to the string type:
Imports System.Runtime.CompilerServices
Module Extensions
<Extension> Public Function EqCaseless(This As String, _
Other As String) As Boolean
Return String.Equals(This, Other, _
StringComparison.InvariantCultureIgnoreCase)
End Sub
End Module
Then, I can compare strings caselessly like this:
If input.EqCaseless("splosions") Then End
Monday, November 18, 2013
.NET - Guide to Server Basics, Part II
Now that you have a listener to catch new connections, it's time to actually communicate with them. I recommend another thread for reading in data. We'll use a queue of messages, but we'll need a new class to keep track of messages.
Public Class QueuedMessage
Public Received As DateTime
Public Data As String
Public Sender As ClientCon
Public Sub New(R As DateTime, D As String, S as ClientCon)
Received = R
Data = D
Sender = ClientCon
End Sub
End Class
Now we can make that queue and add data-reading and message-processing loops.
Public ProcessQueue As New List(Of QueuedMessage)
Public Sub ReadData()
Do
For Each c In Cons
Try
If Not c.Connected Then Exit Try
Dim s As String = c.Reader.ReadLine()
Dim msg As New QueuedMessage(Now, s, c)
If Trim(s) <> "" Then ProcessQueue.Add(msg)
Catch ex As Exception
End Try
Next
Loop
End Sub
Public Sub ProcessLines()
Do
If ProcessQueue.Count > 0 Then
Dim d As QueuedMessage = ProcessQueue(0)
'Your handling code here using d
ProcessQueue.RemoveAt(0)
End If
Loop
End Sub
Insert calls to start threads for those loops to the server's initialization sub. For convenience, I recommend having a writing sub in the connection class. Also, soon we'll need a time field to keep track of keep-alives.
Public LastKA As DateTime
Public Sub Send(Data As String)
Writer.WriteLine(Data)
End Sub
Set the timer and give the client a little head start in the client acceptance code:
c.LastKA = Now.AddSeconds(5)
With that ready, insert a line into the data-reading loop that resets that field when data is received.
c.LastKA = Now
Prepare to enforce the timeout.
Public Sub Discon(Con As ClientCon)
Con.Client.Close()
Cons.Remove(Con)
End Sub
Finally, add a check loop sub.
Public Sub CheckKA()
Do
For Each c In Cons
If c.LastKA.AddSeconds(10) < Now Then Discon(c)
Next
Loop
End Sub
Just make sure to have the client send updates or useless KA messages once every few seconds. I'm not sure what I'll cover next time.
Public Class QueuedMessage
Public Received As DateTime
Public Data As String
Public Sender As ClientCon
Public Sub New(R As DateTime, D As String, S as ClientCon)
Received = R
Data = D
Sender = ClientCon
End Sub
End Class
Now we can make that queue and add data-reading and message-processing loops.
Public ProcessQueue As New List(Of QueuedMessage)
Public Sub ReadData()
Do
For Each c In Cons
Try
If Not c.Connected Then Exit Try
Dim s As String = c.Reader.ReadLine()
Dim msg As New QueuedMessage(Now, s, c)
If Trim(s) <> "" Then ProcessQueue.Add(msg)
Catch ex As Exception
End Try
Next
Loop
End Sub
Public Sub ProcessLines()
Do
If ProcessQueue.Count > 0 Then
Dim d As QueuedMessage = ProcessQueue(0)
'Your handling code here using d
ProcessQueue.RemoveAt(0)
End If
Loop
End Sub
Insert calls to start threads for those loops to the server's initialization sub. For convenience, I recommend having a writing sub in the connection class. Also, soon we'll need a time field to keep track of keep-alives.
Public LastKA As DateTime
Public Sub Send(Data As String)
Writer.WriteLine(Data)
End Sub
Set the timer and give the client a little head start in the client acceptance code:
c.LastKA = Now.AddSeconds(5)
With that ready, insert a line into the data-reading loop that resets that field when data is received.
c.LastKA = Now
Prepare to enforce the timeout.
Public Sub Discon(Con As ClientCon)
Con.Client.Close()
Cons.Remove(Con)
End Sub
Finally, add a check loop sub.
Public Sub CheckKA()
Do
For Each c In Cons
If c.LastKA.AddSeconds(10) < Now Then Discon(c)
Next
Loop
End Sub
Just make sure to have the client send updates or useless KA messages once every few seconds. I'm not sure what I'll cover next time.
Sunday, November 17, 2013
.NET - Guide to Server Basics, Part I
It's moderately difficult to create a server application for the first time. This guide should help you establish a listener and an array of connections.
First, you're going to need to import System.Net.Sockets and System.IO. System.Threading is useful if you want a multithreaded listener. I recommend creating a class to hold connection objects.
Class ClientCon
Public Client As TcpClient
Public Stream As NetworkStream
Public Reader As StreamReader
Public Writer As StreamWriter
End Class
Dim Cons As New List(Of ClientCon)
The collection of connections is helpful to check all the clients in one sweep. Declare a listener as a TcpListener. I usually create a server class to manage all the servery stuff.
Class Server
Public ShutDown As Boolean
Public Listen As TcpListener
Public Sub StartListening(Port As Integer)
Listen = New TcpListener(IPAddress.Any, Port)
Listen.Start()
ThreadPool.QueueUserWorkItem( _
New WaitCallback(AddressOf Listener))
End Sub
Public Sub Listener()
Do
If Listen.Pending() Then
Dim c As New ClientCon
c.Client = Listen.AcceptTcpClient()
c.Stream = c.Client.GetStream()
c.Reader = New StreamReader(c.Stream)
c.Writer = New StreamWriter(c.Stream)
c.Writer.AutoFlush = True
Cons.Add(c)
End If
Loop
End Sub
End Class
Creating a new instance of that class and telling it to start listening will create a new thread that listens for client connections, accepts them, and puts them in the connection list.
Next post, we'll get some communication and keep-alives going.
First, you're going to need to import System.Net.Sockets and System.IO. System.Threading is useful if you want a multithreaded listener. I recommend creating a class to hold connection objects.
Class ClientCon
Public Client As TcpClient
Public Stream As NetworkStream
Public Reader As StreamReader
Public Writer As StreamWriter
End Class
Dim Cons As New List(Of ClientCon)
The collection of connections is helpful to check all the clients in one sweep. Declare a listener as a TcpListener. I usually create a server class to manage all the servery stuff.
Class Server
Public ShutDown As Boolean
Public Listen As TcpListener
Public Sub StartListening(Port As Integer)
Listen = New TcpListener(IPAddress.Any, Port)
Listen.Start()
ThreadPool.QueueUserWorkItem( _
New WaitCallback(AddressOf Listener))
End Sub
Public Sub Listener()
Do
If Listen.Pending() Then
Dim c As New ClientCon
c.Client = Listen.AcceptTcpClient()
c.Stream = c.Client.GetStream()
c.Reader = New StreamReader(c.Stream)
c.Writer = New StreamWriter(c.Stream)
c.Writer.AutoFlush = True
Cons.Add(c)
End If
Loop
End Sub
End Class
Creating a new instance of that class and telling it to start listening will create a new thread that listens for client connections, accepts them, and puts them in the connection list.
Next post, we'll get some communication and keep-alives going.
Tuesday, October 22, 2013
VB .NET - Lambdas
I have recently discovered that lambdas are amazing things. They take a little bit of getting used to, but once mastered, correct use of them will save programmer time and increase program efficiency. A lambda, essentially, is a function or subroutine written in one line that can be passed as an argument, usually in a LINQ method. When a method expects an argument of type Func or Sub (like IEnumerable.ForEach), it's time for a lambda. Start a lambda by writing Function or Sub and an argument in parentheses without type - the object that's going to be put in as the input of the lambda method. Then, simply write the return value. The simplest lambda is the identity lambda: Function(n) n. It takes in object n and returns it. For a Sub, you obviously won't have a return value, so just write what action it performs. Sub(n) MsgBox(n.ToString) accepts object n and writes its string representation in a message box. If you are using more complicated methods, use an AddressOf pointer to indicate that you want to pass the objects through a declared method.
Tuesday, October 15, 2013
Advanced Programming with Twisted Oak Studios
Twisted Oak Studios is a game development consultation firm that also does all manner of other high-level and complicated programming things that I do not understand. Over the past few weeks, I've read every blog article they've ever published. I've learned a lot, especially about functional programming ideas. I highly recommend that you read all the articles on their blog.
Sunday, October 6, 2013
Essay: Classifying Programming Languages
I wrote a classification essay for writing class entitled Classifying Programming Languages. It's in MLA format; signal phrases are used because the teacher said so. Download (DOCX), 15KB.
Tuesday, September 24, 2013
LINQ - Get First Unused ID
I just spent two hours pounding my head against the proverbial wall trying to get some Logofrag stuff to work correctly. After messing around with all manner of methods, I found an amazing StackOverflow answer that put all the pieces in place. This is how I finally was able to find the first unused permission ID:
Dim idsQuery = From right In data.ThreadRights Select right.RightID
Return (From n In idsQuery Where (Not idList.Select(Function(x) x).Contains(n + 1)) Order By n Select n + 1).First
I had never before used lambdas, so that's another thing I learned. They're very easy, just say whether it's a function or sub, give the arguments it takes, and write the return value.
Dim idsQuery = From right In data.ThreadRights Select right.RightID
Return (From n In idsQuery Where (Not idList.Select(Function(x) x).Contains(n + 1)) Order By n Select n + 1).First
I had never before used lambdas, so that's another thing I learned. They're very easy, just say whether it's a function or sub, give the arguments it takes, and write the return value.
Saturday, September 21, 2013
Visual Basic .NET - Binary Operations
In Visual Basic, I sometimes start missing Java's nice bit operators, especially when I really need to pack boolean data into a single number. Fortunately, VB has this; it's just less intuitive. The operator for logical functions is indistinguishable from that of bit functions. So, I can just do this:
For x = 0 To 7
ChecklistPunctuation.Items(x).Selected = ((t.Punctuation And 2 ^ x) <> 0)
Next
Less fortunately, VB does not have shift operators. You can however make do with integer division, multiplication, and exponents of two. To shift a collection of bits N to the left, do this:
shifted = (bitcol \ (2 ^ N))
Maybe it's true that the language wasn't intended to be used in advanced/professional environments, but it certainly has been tortured into providing pretty much any functionality you could want.
For x = 0 To 7
ChecklistPunctuation.Items(x).Selected = ((t.Punctuation And 2 ^ x) <> 0)
Next
Less fortunately, VB does not have shift operators. You can however make do with integer division, multiplication, and exponents of two. To shift a collection of bits N to the left, do this:
shifted = (bitcol \ (2 ^ N))
Maybe it's true that the language wasn't intended to be used in advanced/professional environments, but it certainly has been tortured into providing pretty much any functionality you could want.
Subscribe to:
Posts (Atom)